13544 Commits

Author SHA1 Message Date
Niels Beier b6fc656edc fix(pubsub): defer group transport validation until server startup (#8583)
* fix(pubsub): defer group transport validation until server startup

* test(pubsub): add a writer group with a UDP address before server startup
2026-10-16 13:22:31 +02:00
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 54fcc04ccc fix(drivers): only warn when the GDS role setup fails
UA_GDSReceiver_start did not start when initGDSRolePermissions failed,
but left the push method callbacks installed. The RolePermissions are
one way to restrict the methods to SecurityAdmin (Part 12, 7.10). RBAC
is optional, and the AccessControl plugin can restrict the methods as
well. So a failed role setup is now logged as a warning and the receiver
starts.
2026-10-13 08:55:20 +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 a59a620301 refactor(drivers): name namespace zero source consistently (#8563)
Co-authored-by: Julius Pfrommer <julius.pfrommer@o6-automation.com>
2026-10-12 19:26:17 +02:00
Julius Pfrommer 303cd4d5a6 fix(client): validate discovery requests against the connection URL
Avoid comparing an unset optional endpoint override with strncmp. Use an exact UA_String comparison against the configured connection URL for all three discovery helpers, and initialize result outputs before early errors.

Cover matching URLs with session and channel-only connections, and reject different hosts and URL prefixes. Validated discovery, server node services, server lifecycle and event loop suites under ASan/UBSan.
2026-10-12 17:08:39 +02:00
Julius Pfrommer 6f003c5fee fix(arch): match lwIP socket callback signatures 2026-10-12 17:08:39 +02:00
Julius Pfrommer 120f74136e fix(arch): match lwIP lifecycle callback signatures 2026-10-12 17:08:39 +02:00
Julius Pfrommer 513ca790ae test(arch): cover lwIP interface lifetimes and add sanitizer CI 2026-10-12 17:08:39 +02:00
Julius Pfrommer 9bb9b345df fix(arch): give the default lwIP TAP interface process lifetime 2026-10-12 17:08:39 +02:00
Julius Pfrommer e0034851de fix(server): start queued reverse connections after event loop startup 2026-10-12 17:08:39 +02:00
Julius Pfrommer 7765769e5a test(plugin): verify and document shared event loop ownership 2026-10-12 17:08:39 +02:00
Julius Pfrommer 4be0e4c6f4 refactor(pubsub): defer transport activation until server startup
Allow PubSub connections to be configured and enabled while the event loop and connection
managers are still fresh. Defer transport validation and socket creation until the server
starts. Enabled connections remain paused during configuration and automatically transition to
operational once the PubSub manager becomes active.

Add lifecycle coverage for creating and enabling a UDP PubSub connection before server startup.
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 ef4c4b6a54 docs(driver): document file transfer and custom backends
Add the file transfer tutorial, a custom in-memory backend example, and
build and feature documentation. Wire the public API and tutorial into
the generated driver documentation.

Co-authored-by: Julius Pfrommer <julius.pfrommer@o6-automation.com>
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
Andreas Ebner bd7a445560 feat(driver): add local file and directory backends
Serve a single file or a directory using the shared file backend operations.
Validate relative paths, create entries exclusively, and avoid replacing an
existing rename destination. Support Windows UTF-16 paths and filter links
to directories and special files during enumeration.

Co-authored-by: Julius Pfrommer <julius.pfrommer@o6-automation.com>
2026-10-12 00:14:33 +02:00
Andreas Ebner 5514c756e6 feat(driver): add per-backend Part 20 file transfer drivers
Expose one file or directory backend through FileType and FileDirectoryType
Objects. Bind Objects to the driver lifetime, preserve reused application
nodes, and close session handles on stop or session cleanup.

Enforce file and directory access rights in Properties and Methods. Index
entries and handles with zip trees, reconcile storage changes, and retain
removed entries while their handles remain open. Include the reduced
namespace model and compile the driver with Method-call support in every
Namespace Zero configuration. Export the API unconditionally.

Co-authored-by: Julius Pfrommer <julius.pfrommer@o6-automation.com>
2026-10-12 00:14:33 +02:00
Julius Pfrommer 27dad4638f fix(server): reject duplicate driver registration
Reject registering the same driver twice, which would create a cycle in the
server's driver list. Permit multiple generic drivers and enforce a single
instance for each specialized driver type.
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 6110aeb67a Merge pull request #8548 from o6-automation/merge_15_master
Merge 1.5 to master
2026-10-08 16:54:11 +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
Niels Beier 797187ec9f fix(server): return BadSessionIdInvalid for CloseSession without a bound Session
Part 4, 5.7.4.3 lists only Bad_SessionIdInvalid for CloseSession. Rewrite all StatusCodes except Bad_SessionClosed.
2026-10-08 14:28:43 +02:00
Andreas Ebner 1e4eff47c8 fix(core): derive only the signing key for ECC Sign without AEAD
Part 6 6.8.1 excludes the encrypting key and IV from L for ECC policies
without AEAD in SecurityMode Sign. We derived the full length, so Sign
with nistP256/384 and brainpool failed with other stacks. Adds a test.
2026-10-07 21:54:14 +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