Blob Blame History Raw
From 1bb93f70de9907d88b2ebc5c6ffee14417d90fee Mon Sep 17 00:00:00 2001
From: Anton Bobrov <antbob@users.noreply.github.com>
Date: Mon, 19 Sep 2022 17:51:07 +0200
Subject: [PATCH] SUDO: Fix timezone issues with sudoNotBefore and sudoNotAfter
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The current code does not respect generalized time as specified in related before/after attributes.
The problem with the current implementation is that it essentially treats them as local time,
with no regard to TZ and DST.

This patch is using timegm(3) instead of mktime(3) to address said timezone issues and some bare
minimum static unit tests with known verified values to make sure the API is consitent with them.

Resolves:
https://github.com/SSSD/sssd/issues/6354

Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
(cherry picked from commit 0198f64ce231e9608b14152c64426fb9e015fd33)
---
 configure.ac                       |  3 +++
 src/db/sysdb_sudo.c                | 11 ++++++++++-
 src/tests/cmocka/test_sysdb_sudo.c | 29 +++++++++++++++++++++++++++++
 3 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 89abddef4..a90e16642 100644
--- a/configure.ac
+++ b/configure.ac
@@ -92,6 +92,9 @@ LIBS=$SAVE_LIBS
 AC_CHECK_FUNCS([ utimensat \
                  futimens ])
 
+# Check for the timegm() function (not part of POSIX / Open Group specs)
+AC_CHECK_FUNC([timegm], [], [AC_MSG_ERROR([timegm() function not found])])
+
 #Check for endian headers
 AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h])
 
diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c
index 59d6824c0..3a918e9c6 100644
--- a/src/db/sysdb_sudo.c
+++ b/src/db/sysdb_sudo.c
@@ -60,9 +60,18 @@ static errno_t sysdb_sudo_convert_time(const char *str, time_t *unix_time)
     for (format = formats; *format != NULL; format++) {
         /* strptime() may leave some fields uninitialized */
         memset(&tm, 0, sizeof(struct tm));
+        /* Let underlying implementation figure out DST */
+        tm.tm_isdst = -1;
         tret = strptime(str, *format, &tm);
         if (tret != NULL && *tret == '\0') {
-            *unix_time = mktime(&tm);
+            /* Convert broken-down time to local time */
+            if (tm.tm_gmtoff == 0) {
+                *unix_time = timegm(&tm);
+            } else {
+                long offset = tm.tm_gmtoff;
+                tm.tm_gmtoff = 0;
+                *unix_time = timegm(&tm) - offset;
+            }
             return EOK;
         }
     }
diff --git a/src/tests/cmocka/test_sysdb_sudo.c b/src/tests/cmocka/test_sysdb_sudo.c
index fc6a47a16..f852427fd 100644
--- a/src/tests/cmocka/test_sysdb_sudo.c
+++ b/src/tests/cmocka/test_sysdb_sudo.c
@@ -44,6 +44,12 @@
 #define OVERRIDE_GROUP_NAME "group_sudo_test"
 #define OVERRIDE_UID 2112
 
+/* sysdb_sudo_convert_time function is static */
+extern char *strptime(const char *__restrict __s,
+                      const char *__restrict __fmt,
+                      struct tm *__tp);
+#include "src/db/sysdb_sudo.c"
+
 struct test_user {
     const char *name;
     uid_t uid;
@@ -949,6 +955,26 @@ void test_filter_rules_by_time(void **state)
     talloc_zfree(_rules);
 }
 
+void test_sudo_convert_time(void **state)
+{
+    /* Each ctime should map to its corresponding utime */
+    const char *ctimes[] = {"20220715090000Z",
+                            "20220715090000+0200",
+                            "20220715090000-0200"};
+    const time_t utimes[] = {1657875600,
+                             1657868400,
+                             1657882800};
+    const int ntimes = sizeof(ctimes) / sizeof(ctimes[0]);
+    time_t converted;
+    errno_t ret;
+
+    for (int i = 0; i < ntimes; i++) {
+        ret = sysdb_sudo_convert_time(ctimes[i], &converted);
+        assert_int_equal(ret, EOK);
+        assert_int_equal(converted, utimes[i]);
+    }
+}
+
 int main(int argc, const char *argv[])
 {
     int rv;
@@ -1029,6 +1055,9 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(test_filter_rules_by_time,
                                         test_sysdb_setup,
                                         test_sysdb_teardown),
+
+        /* sysdb_sudo_convert_time() */
+        cmocka_unit_test(test_sudo_convert_time)
     };
 
     /* Set debug level to invalid value so we can decide if -d 0 was used. */
-- 
2.37.3