Hi eProsima team — flagging a structural issue in PermissionsParser::parse_validity where credential validity datetimes are parsed as the server's local time, which makes the effective validity window depend on the server's TZ (and DST state). Happy to follow up with a PR; the fix is small.
Where
src/cpp/security/accesscontrol/PermissionsParser.cpp:342 — validity.not_before = std::mktime(&time);
src/cpp/security/accesscontrol/PermissionsParser.cpp:363 — validity.not_after = std::mktime(&time);
Why this is a bug
std::mktime(&tm) interprets the struct tm as local time of the host, returning a time_t in seconds since the Unix epoch. The parsed value therefore depends on:
- The host's
TZ environment / system timezone configuration
- Whether DST is in effect for that date (because the
memset zeroes tm_isdst, behavior is implementation-defined; on many libc's, DST correction still applies during DST periods)
These values are then compared against current_time (a UTC-relative time_t) in Permissions.cpp:
Permissions.cpp:265: if (std::difftime(current_time, validity.not_before) >= 0)
Permissions.cpp:267: if (std::difftime(validity.not_after, current_time) >= 0)
So the access-control decision is comparing a UTC current_time against a local-time-shifted not_before / not_after. The window is offset by the host TZ (and DST). For example, an identical <not_after>2030-01-01T00:00:00</not_after> in a permissions file results in:
- ~8 hours longer validity on a host in
America/Los_Angeles (UTC-8 in winter)
- ~8 hours shorter validity on a host in
Asia/Singapore (UTC+8)
- Off-by-one-hour around DST transitions on hosts in DST-observing zones
Why this matters
Permissions XML files are portable artifacts signed by a Permissions CA and intended to be distributed across the DDS domain. The CA cannot know the local timezone of every host that will consume the file, so an honest interpretation of CA intent is "UTC unless otherwise specified." Local-time parsing is non-portable and can shift effective credential validity by hours.
This isn't an immediate exploit so much as a credential-lifecycle correctness bug: a CA revoking a permission by issuing one with an earlier <not_after> may discover the revocation is silently delayed by the host's TZ offset, and vice versa.
Suggested fix
Use a UTC-based conversion path. On POSIX, timegm(3) is the symmetric counterpart to mktime that interprets the struct tm as UTC. On Windows, _mkgmtime is the equivalent. C++20 also has std::chrono::utc_clock and std::chrono::parse with explicit UTC handling.
#if defined(_WIN32)
validity.not_before = _mkgmtime(&time);
#else
validity.not_before = timegm(&time);
#endif
The same change in both call sites. Additionally, std::get_time(&time, "%Y-%m-%dT%T") does not consume an optional Z or ±HH:MM suffix; if the XML actually includes a timezone designator, it is silently ignored. Worth a docstring/comment clarifying the expected format.
Spec note
The OMG DDS-Security spec uses xsd:dateTime for validity fields. xsd:dateTime with no timezone designator is formally "timezone-unspecified," but security-relevant uses of xsd:dateTime are typically interpreted as UTC for portability reasons (see XMLDSig, SAML, etc.). The Fast-DDS test fixtures (and Cyclone DDS examples) use timezone-unspecified strings like 2015-09-15T01:00:00, which is exactly the case where this bug bites.
Happy to PR a timegm / _mkgmtime switch + tests in different TZs if that helps. Thanks for the project.
Hi eProsima team — flagging a structural issue in
PermissionsParser::parse_validitywhere credential validity datetimes are parsed as the server's local time, which makes the effective validity window depend on the server's TZ (and DST state). Happy to follow up with a PR; the fix is small.Where
src/cpp/security/accesscontrol/PermissionsParser.cpp:342—validity.not_before = std::mktime(&time);src/cpp/security/accesscontrol/PermissionsParser.cpp:363—validity.not_after = std::mktime(&time);Why this is a bug
std::mktime(&tm)interprets thestruct tmas local time of the host, returning atime_tin seconds since the Unix epoch. The parsed value therefore depends on:TZenvironment / system timezone configurationmemsetzeroestm_isdst, behavior is implementation-defined; on many libc's, DST correction still applies during DST periods)These values are then compared against
current_time(a UTC-relativetime_t) inPermissions.cpp:Permissions.cpp:265:if (std::difftime(current_time, validity.not_before) >= 0)Permissions.cpp:267:if (std::difftime(validity.not_after, current_time) >= 0)So the access-control decision is comparing a UTC
current_timeagainst a local-time-shiftednot_before/not_after. The window is offset by the host TZ (and DST). For example, an identical<not_after>2030-01-01T00:00:00</not_after>in a permissions file results in:America/Los_Angeles(UTC-8 in winter)Asia/Singapore(UTC+8)Why this matters
Permissions XML files are portable artifacts signed by a Permissions CA and intended to be distributed across the DDS domain. The CA cannot know the local timezone of every host that will consume the file, so an honest interpretation of CA intent is "UTC unless otherwise specified." Local-time parsing is non-portable and can shift effective credential validity by hours.
This isn't an immediate exploit so much as a credential-lifecycle correctness bug: a CA revoking a permission by issuing one with an earlier
<not_after>may discover the revocation is silently delayed by the host's TZ offset, and vice versa.Suggested fix
Use a UTC-based conversion path. On POSIX,
timegm(3)is the symmetric counterpart tomktimethat interprets thestruct tmas UTC. On Windows,_mkgmtimeis the equivalent. C++20 also hasstd::chrono::utc_clockandstd::chrono::parsewith explicit UTC handling.The same change in both call sites. Additionally,
std::get_time(&time, "%Y-%m-%dT%T")does not consume an optionalZor±HH:MMsuffix; if the XML actually includes a timezone designator, it is silently ignored. Worth a docstring/comment clarifying the expected format.Spec note
The OMG DDS-Security spec uses
xsd:dateTimefor validity fields.xsd:dateTimewith no timezone designator is formally "timezone-unspecified," but security-relevant uses ofxsd:dateTimeare typically interpreted as UTC for portability reasons (see XMLDSig, SAML, etc.). The Fast-DDS test fixtures (and Cyclone DDS examples) use timezone-unspecified strings like2015-09-15T01:00:00, which is exactly the case where this bug bites.Happy to PR a
timegm/_mkgmtimeswitch + tests in different TZs if that helps. Thanks for the project.