Skip to content

Commit c378ca0

Browse files
committed
fix: interpret permission validity timestamps as UTC
std::mktime interprets timezone-unspecified broken-down time as local time, causing identical permission files to produce different validity periods depending on the host timezone. Use timegm on POSIX and _mkgmtime on Windows so the currently supported timezone-unspecified timestamp format is interpreted consistently as UTC. This change does not add parsing for explicit Z or +/-HH:MM timezone suffixes. Fixes #6403. Signed-off-by: Functionhx <2994114386@qq.com> Signed-off-by: Yuchen Fan <functionhx@gmail.com>
1 parent dd66ef2 commit c378ca0

2 files changed

Lines changed: 107 additions & 2 deletions

File tree

src/cpp/security/accesscontrol/PermissionsParser.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <iostream>
2121
#include <sstream>
2222
#include <iomanip>
23+
#include <ctime>
2324

2425
#if TIXML2_MAJOR_VERSION >= 6
2526
#define PRINTLINE(node) node->GetLineNum()
@@ -339,7 +340,11 @@ bool PermissionsParser::parse_validity(
339340

340341
if (!stream.fail())
341342
{
342-
validity.not_before = std::mktime(&time);
343+
#ifdef _WIN32
344+
validity.not_before = _mkgmtime(&time);
345+
#else
346+
validity.not_before = timegm(&time);
347+
#endif // ifdef _WIN32
343348
#endif // if _MSC_VER != 1800
344349

345350
tinyxml2::XMLElement* old_node = node;
@@ -360,7 +365,11 @@ bool PermissionsParser::parse_validity(
360365

361366
if (!stream.fail())
362367
{
363-
validity.not_after = std::mktime(&time);
368+
#ifdef _WIN32
369+
validity.not_after = _mkgmtime(&time);
370+
#else
371+
validity.not_after = timegm(&time);
372+
#endif // ifdef _WIN32
364373
#endif // if _MSC_VER != 1800
365374
returned_value = true;
366375
}

test/unittest/security/accesscontrol/AccessControlTests.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include <cstdlib>
16+
#include <cstring>
1517
#include <iostream>
1618
#include <string>
1719

1820
#include <gtest/gtest.h>
1921

22+
#include <security/accesscontrol/PermissionsParser.h>
23+
2024
#include <rtps/builtin/data/ParticipantProxyData.hpp>
2125
#include <rtps/builtin/data/ReaderProxyData.hpp>
2226
#include <rtps/builtin/data/WriterProxyData.hpp>
@@ -558,6 +562,98 @@ TEST_F(AccessControlTest, participant_creation_fail_with_empty_topic_expression_
558562

559563
}
560564

565+
/* Regression test for eProsima/Fast-DDS#6403.
566+
*
567+
* Verify that permission validity timestamps are interpreted as UTC
568+
* regardless of the host timezone.
569+
*/
570+
#if _MSC_VER != 1800
571+
TEST(AccessControlTimestampTest, permission_timestamps_utc_independent_of_tz)
572+
{
573+
struct TzGuard
574+
{
575+
std::string original;
576+
TzGuard(
577+
const std::string& tz_value)
578+
{
579+
const char* tz = std::getenv("TZ");
580+
if (tz)
581+
{
582+
original = tz;
583+
}
584+
#ifdef _WIN32
585+
_putenv_s("TZ", tz_value.c_str());
586+
_tzset();
587+
#else
588+
setenv("TZ", tz_value.c_str(), 1);
589+
tzset();
590+
#endif
591+
}
592+
~TzGuard()
593+
{
594+
if (original.empty())
595+
{
596+
#ifdef _WIN32
597+
_putenv_s("TZ", "");
598+
#else
599+
unsetenv("TZ");
600+
#endif
601+
}
602+
else
603+
{
604+
#ifdef _WIN32
605+
_putenv_s("TZ", original.c_str());
606+
#else
607+
setenv("TZ", original.c_str(), 1);
608+
#endif
609+
}
610+
#ifdef _WIN32
611+
_tzset();
612+
#else
613+
tzset();
614+
#endif
615+
}
616+
};
617+
618+
// Set a non-UTC timezone.
619+
TzGuard tz_guard("UTC-8");
620+
621+
const char* xml =
622+
"<dds>"
623+
"<permissions>"
624+
"<grant name=\"test_grant\">"
625+
"<subject_name>CN=Test</subject_name>"
626+
"<validity>"
627+
"<not_before>2015-09-15T01:00:00</not_before>"
628+
"<not_after>2025-09-15T01:00:00</not_after>"
629+
"</validity>"
630+
"<allow_rule>"
631+
"<domains><id>0</id></domains>"
632+
"<publish><topics><topic>test_topic</topic></topics></publish>"
633+
"</allow_rule>"
634+
"<default>ALLOW</default>"
635+
"</grant>"
636+
"</permissions>"
637+
"</dds>";
638+
639+
PermissionsParser parser;
640+
PermissionsData permissions;
641+
642+
ASSERT_TRUE(parser.parse_stream(xml, std::strlen(xml)));
643+
parser.swap(permissions);
644+
645+
ASSERT_EQ(permissions.grants.size(), 1u);
646+
647+
// 2015-09-15T01:00:00 UTC = 1442278800
648+
const std::time_t expected_not_before = 1442278800;
649+
EXPECT_EQ(permissions.grants[0].validity.not_before, expected_not_before);
650+
651+
// 2025-09-15T01:00:00 UTC = 1757898000
652+
const std::time_t expected_not_after = 1757898000;
653+
EXPECT_EQ(permissions.grants[0].validity.not_after, expected_not_after);
654+
}
655+
#endif // _MSC_VER != 1800
656+
561657
int main(
562658
int argc,
563659
char** argv)

0 commit comments

Comments
 (0)