691 Commits

Author SHA1 Message Date
Andreas Ebner 90effffe86 feat(driver): close file handles without activity after a timeout
The activity-timeout parameter closes handles without a Method call like Close,
e.g. for the ActivityTimeout of a TrustList (Part 12 7.8.2.1).
2026-10-15 22:05:04 +02:00
Andreas Ebner 0c253ab297 feat(driver): report an unknown file size as Bad_NotSupported (Part 20 4.2.1)
A backend can mark the size in the FileInfo as unknown, e.g. for content that
is generated on Open like a TrustList (Part 12 7.8.2.1).
2026-10-15 22:05:04 +02:00
Andreas Ebner c1180cdb96 feat(driver): open file handles from FileType subtype Methods
UA_FileTransferDriver_openHandle creates a Session handle with the checks of
Open, e.g. for OpenWithMasks of a TrustList (Part 12). Its openContext reaches
the backend open.
2026-10-15 22:05:04 +02:00
Andreas Ebner 0503c8fd97 feat(driver): open files with the Session of the caller (Part 20 4.2.2)
The backend open gets the caller as a context: the Session of Open, CreateFile
and the copies of MoveOrCopy, NULL for direct calls. Its result reaches the
client.
2026-10-15 22:05:04 +02:00
Andreas Ebner 3350689aed fix(server): name session and subscription diagnostics in ns=1 (Part 3, 8.3)
The server defines these names, so they don't belong to namespace 0. The
children keep the ns=0 BrowseNames of their types (Part 3, 6.4.1).
2026-10-15 08:39:58 +02:00
Julius Pfrommer 447957ee4e test(server): expect session cleanup during shutdown 2026-10-14 21:01:31 +02:00
Julius Pfrommer 225195268a fix(server): defer session cleanup through shutdown 2026-10-14 21:01:31 +02:00
Andreas Ebner 58ff015aa8 test(driver): use a retained descriptor for file permission changes
Replace pathname-based chmod calls in localWriteOnlyFile with fchmod on a retained descriptor, addressing the three CodeQL TOCTOU comments. Close the descriptor before removing the scratch directory.
2026-10-14 08:11:28 +02:00
Andreas Ebner a83ee91bf9 fix(driver): preserve Object contexts and reject incomplete file listings
Restore application contexts before subtree deletion and keep original ownership separate from explicit Delete/move operations. Preserve contexts allocated by constructors for driver-created Objects as well.

Propagate entry metadata errors so a cross-driver move cannot delete an uncopied source file. Add lifecycle and local filesystem regressions covering both findings.
2026-10-14 08:11:28 +02:00
Andreas Ebner 8d28921d72 fix(driver): reject wrongly typed parameters and document the Method ownership
A known parameter with another type was ignored silently, e.g. read-only as
Int32. The header states that the drivers own the shared Method nodes.
2026-10-14 08:11:28 +02:00
Andreas Ebner 6774f9cc9e fix(driver): reject IndexRange on file Properties and check ancestors for Writable
Size, LastModifiedTime, Writable and UserWritable returned the scalar for an
IndexRange (Part 4 7.27). Writable and UserWritable ignored a denied Traverse
on a managed ancestor that Open enforces (Part 20 4.2.1).
2026-10-14 08:11:28 +02:00
Andreas Ebner 6299a405bc fix(driver): delete reused Objects with a client Delete or move (Part 20 4.3.5)
The Delete and MoveOrCopy Methods kept application Objects of the removed
storage entries; teardown and refresh still only release them. The subtree
release iterates the children instead of re-reading the child index.
2026-10-14 08:11:28 +02:00
Andreas Ebner 20fe8e1713 fix(driver): report enumeration errors and open write-only local files
A readdir() error ended the listing early, so a move to another driver could
delete source files that were never copied. Writing a file needs no read
access anymore (Part 20 4.2.2).
2026-10-14 08:11:28 +02:00
Daniel Opitz 4fc9c419f3 fix(server): properly separated sync shutdown from async shutdown (with externally managed eventloop) 2026-10-13 14:05:37 +02:00
Daniel Opitz e95223e142 fix(server): complete shutdown with an external EventLoop 2026-10-13 14:05:37 +02:00
Andreas Ebner 16a739a7c3 fix(server): fail startup when no transport can open a server socket
UA_Server_run_startup ignored the driver results and ran without
listening. It now fails if a transport cannot open its socket and
neither another transport nor a reverse connect reaches the server.
2026-10-13 08:55:20 +02:00
Julius Pfrommer 7765769e5a test(plugin): verify and document shared event loop ownership 2026-10-12 17:08:39 +02:00
Julius Pfrommer 617cbcb2ec refactor(plugin): defer default event loop startup until use 2026-10-12 17:08:39 +02:00
Andreas Ebner 5d325c6e16 test(driver): run the file transfer tests with lwIP and ThreadSanitizer
The local backend tests are skipped where the backend is a stub; the client
test stops its server thread with an atomic flag.
2026-10-12 00:14:33 +02:00
Andreas Ebner a109401823 feat(driver): resolve file handles for FileType subtype methods
Methods like CloseAndUpdate of the PubSubConfiguration (Part 14) map the
client FileHandle to the backend handle and close it as Close does.
2026-10-12 00:14:33 +02:00
Andreas Ebner 0d36d460b5 fix(driver): keep file handles and Read results consistent (Part 20 4.2)
Handles survive a vanished file or a replaced parent, Read at the end returns an
empty ByteString, the root is queried with the complete backend, failed starts
are logged and the analyzer accepts releaseSubtree.
2026-10-12 00:14:33 +02:00
Andreas Ebner edfbf9516e feat(driver): make the directory refresh optional and enforce the scan limits
refresh-interval 0 disables the timer, UA_FileTransferDriver_refresh() reconciles
on demand. The directory Methods respect max-scan-depth; skipped entries and cut
trees are logged at start and when they change.
2026-10-12 00:14:33 +02:00
Andreas Ebner d3dc57007c test(driver): cover file transfer lifecycle, methods, and backends
Exercise backend contracts, access rights, session isolation, handle limits,
reconciliation, partial failures, reused-node restoration, shared Methods,
and independent driver lifecycles. Include client/server transfers and
zip-tree cleanup regressions. Build the suites when Method calls and the
required Namespace Zero types are available.

Co-authored-by: Julius Pfrommer <julius.pfrommer@o6-automation.com>
2026-10-12 00:14:33 +02:00
Julius Pfrommer 7a6aca9e54 fix(server): propagate driver startup errors when adding at runtime
Return the start callback's error when a driver is added to a running server.
Unlink a failed registration, retain its server pointer for cleanup, and leave
ownership with the caller. Cover failure, retry, and shutdown in server tests.
2026-10-12 00:14:33 +02:00
ChiouYouJia 86dd0be440 fix(server): do not leak the user token secret through the ActivateSession audit
ActivateSession decrypts the UserIdentityToken in place (decryptUserToken). Afterwards
auditActivateSessionEvent put that token unchanged into the audit payload, so the
payload carried the plaintext password of a UserNameIdentityToken (also when it was
sent RSA-OAEP encrypted on a SignAndEncrypt channel) or the plaintext tokenData of an
IssuedIdentityToken.

The payload is used in two places, and both leaked the secret:
- the auditNotificationCallback / globalNotificationCallback of the application;
- the event fields of the AuditActivateSessionEventType event that is generated on
  the Server object. A MonitoredItem selecting /UserIdentityToken receives the
  password of every user who logs in. Audit events are meant to be collected by
  audit/SIEM clients, so the secret leaves the server and ends up in logs.

This affects master only (the auditing code is not in a release branch) and only
servers with auditingEnabled.

Put a shallow copy of the token without password / tokenData into the payload. The
copy lives on the stack of auditActivateSessionEvent and outlives the synchronous
auditSessionEvent call; no allocation. PolicyId, UserName and EncryptionAlgorithm
are kept for the audit trail. Other token types are unchanged.

The test logs in as user1 and checks both the audit notification and the
AuditActivateSessionEventType event received by a local event MonitoredItem on the
Server object: they name user1 and carry no password. Without the fix both carried
the password.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:00:29 +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
Niels Beier a644205dd3 test(server): cover CloseSession from a foreign SecureChannel 2026-10-08 14:28:43 +02:00
Andreas Ebner bf8d24e0cf fix(server): offer user tokens on #None only if they can be secured
Without encryption support, the #None endpoint offered x509 and password
tokens that ActivateSession always rejects. The endpoint check now runs in
every build. It removes x509 tokens without the password warning.
2026-10-06 18:22:49 +02:00
Andreas Ebner 8572ffcd16 fix(server): treat a null and an empty locale as the same
UA_Node_insertOrUpdateLocale told a null locale from an empty one, so a
DisplayName or Description written with one could not be updated or removed
with the other. Part 3, 8.4 treats both as the unknown locale.
2026-10-06 18:22:49 +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
Andreas Ebner 5aa2e35abe fix(server): reject an access control without its mandatory hooks
The services call six AccessControl callbacks without a NULL check, so a
plugin without one of them crashed the server on the first request.
UA_Server_run_startup now refuses such a plugin with BadConfigurationError.
2026-10-06 18:22:49 +02:00
Andreas Ebner 88153c4920 fix(server): remove a connected reverse connect only once
UA_Server_removeReverseConnect unlinked a connected entry and its CLOSING
callback unlinked it again, which corrupted the list. The entry now stays
linked until its connection has closed.
2026-10-06 18:22:49 +02:00
Andreas Ebner a6ec83db51 fix(server): use the external notifications of external value sources
Read and Write took onRead/onWrite of an external value source from
valueSource.internal, but the setter stores them in valueSource.external at
a different offset of the union. So they never fired.
2026-10-06 18:22:49 +02:00
StalderT ed5d86df4d fix(server): drop links to deleted MonitoredItems in SetTriggering
Links to a deleted MonitoredItem were only removed when the triggering item reported. If it never reports (Disabled mode, or a value that does not change), they stay forever, and since MonitoredItem ids are never reused a client can grow the link array of one item without bound by creating, linking and deleting items in a loop. Every addLink scans that array, so SetTriggering gets slower with each round.

Part 4, 5.13.1.6 says that the link goes away with the triggered item. SetTriggering now prunes the links of the triggering item to MonitoredItems that no longer exist before applying the request, which keeps the array bounded by the items of the Subscription.
2026-10-06 12:47:29 +02:00
Niels Beier bc51e62537 test(server): check the end of the first publishing cycle 2026-10-06 09:18:05 +02:00
Julius Pfrommer 88e72ab86f test(server): wait for async request delivery with lwIP 2026-10-06 07:37:13 +02:00
Niels Beier edc16fb6f2 test(server): cover transfer of detached Subscriptions 2026-10-06 07:37:13 +02:00
StalderT b2eae285f9 fix(server): limit the number of links in SetTriggering
CreateMonitoredItems, ModifyMonitoredItems, SetMonitoringMode and DeleteMonitoredItems all check maxMonitoredItemsPerCall, but SetTriggering accepted any number of links. Apply the same limit there, counting the links to add and the links to remove together, and return Bad_TooManyOperations above it (Part 4, 5.13.5).

Also free removeResults with its own size in the out-of-memory path, it was using linksToAddSize.
2026-10-05 23:35:14 +02:00
Niels Beier 8f7d39b798 test(server): cover Write status codes of non-writable attributes 2026-10-05 23:33:13 +02:00
Niels Beier 32c514b4c7 test(server): check the StatusChange after a transfer without Publish 2026-10-05 23:30:06 +02:00
Julius Pfrommer 7cc1abc1ce test: Use atomic stores in browse access-control regression
Match the server loop atomic reads when starting and stopping the test server. This fixes the ThreadSanitizer race introduced by the 1.5 merge.

Validation: reproduced the race locally before the fix; check_accesscontrol passes with ThreadSanitizer after the fix.
2026-10-04 18:45:43 +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
Julius Pfrommer 2a70e31f17 Merge remote-tracking branch 'origin/1.4' into merge_14_15
Adapt the incoming fixes and regressions to the 1.5 encoding options,
nodestore, connection-manager, PubSub and session APIs. Preserve the
strict HEL EndpointUrl limit of less than 4096 bytes.

Cover cancellation of interleaved queued Publish requests, including
response handles, cancellation counts and a successful CancelResponse.
Extend the nodestore mask regression to 1.5's derived attributes and
invalid attribute identifiers.

Validation: full OpenSSL test build; 252 checks passed across the builtin
types, server, attribute services, client subscriptions and mbedTLS PubSub
decryption suites, with sanitizers enabled.
2026-10-04 15:02:49 +02:00
Julius Pfrommer dcc3d80b81 fix(server): reject trailing data in HEL messages
Require decoding the HEL body to consume the entire message. Add handshake regressions for one-byte and eight-byte suffixes, checking BadDecodingError and connection closure.
2026-10-04 13:06:33 +02:00
Julius Pfrommer e74c32ef29 fix(server): enforce the HEL EndpointUrl length limit
Reject EndpointUrls longer than 4096 bytes with BadTcpEndpointUrlInvalid and propagate allocation errors when copying the accepted URL. Add handshake tests for the boundary, oversized URLs, connection closure, and negotiation of a higher requested protocol version down to zero.
2026-10-04 13:06:33 +02:00
Christoffer Lind acad45bdfb fix(server): pass the mask of the read attribute to the nodestore
attr2mask is indexed by the AttributeId, but the table starts at index
0 while AttributeIds start at 1. Each Read therefore asks the
Nodestore for the attribute that follows the requested one, for
example DataType instead of Value.
2026-10-02 20:19:56 +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
ChiouYouJia 5ae7657e58 feat(server): add SourceNode to the write audit notification payload
The AUDIT_UPDATE_WRITE application notification carries the AttributeId,
IndexRange, NewValue and OldValue of a write, but not the NodeId of the written
node. That NodeId is only set as the SourceNode of the generated
AuditWriteUpdateEvent, so an auditNotificationCallback cannot tell which node
was written without additionally subscribing to the events.

Add /SourceNode (the BaseEventType field name) to the payload. The generated
events are unchanged: the event-field lookup already resolves /SourceNode to
the same NodeId by default, and the RBAC check uses the EventDescription.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:41:58 +02:00
Niels Beier a8c9c10f9d refactor(server): compute SecureChannel attributes on access, lock ns0
Namespace 0 is now restricted to the predefined set of SecureChannel
attributes, for both read and write; other namespaces stay free-form.
The built-in read-only keys are no longer cached in channel->attributes. UA_SecureChannel_getBuiltinAttribute computes them fresh from the
live channel on every access, and notifySecureChannel now reuses it
instead of duplicating the field mappings.
2026-09-25 13:30:13 +02:00
Niels Beier 17a4ea6e55 feat(server): expose SecureChannel background info as read-only attributes
notifySecureChannel now merges the same data it sends in the
SECURECHANNEL_OPENED notification into channel->attributes, so it's
readable via UA_Server_getSecureChannelAttribute and friends. These
keys are rejected by set/delete with BADNOTWRITABLE, only
maxMessageSize stays writable. Also fixes a null-pointer read on
connectionManager in notifySecureChannel and a misleading doc comment
on getSecureChannelAttribute_scalar.
2026-09-25 13:30:13 +02:00