>From 213f66c136fc2cabe79e1110afbcb7e29c330349 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <preichl@redhat.com>
Date: Tue, 20 Jan 2015 16:27:41 -0500
Subject: [PATCH 1/2] UTIL: convert GeneralizedTime to unix time

New utility function *convert_time* to convert GeneralizedTime to
unix time.
---
 Makefile.am            |  3 ++-
 src/tests/util-tests.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++
 src/util/util.c        | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/util/util.h        |  3 +++
 src/util/util_errors.c |  1 +
 src/util/util_errors.h |  1 +
 6 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/Makefile.am b/Makefile.am
index ffbdf970a5702fcd745611b1ba739d31b4e98c0e..8d557b257c18b62c683e3007c820a1f93ff0595d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1554,7 +1554,8 @@ simple_access_tests_LDADD = \
     libsss_test_common.la
 
 util_tests_SOURCES = \
-    src/tests/util-tests.c
+    src/tests/util-tests.c \
+    src/util/util.c
 util_tests_CFLAGS = \
     $(AM_CFLAGS) \
     $(CHECK_CFLAGS)
diff --git a/src/tests/util-tests.c b/src/tests/util-tests.c
index 94015d8e1e0efb143a4fea998f1b16db1e63365e..bb7ace322f410d3a954306d6bf45772a8638e704 100644
--- a/src/tests/util-tests.c
+++ b/src/tests/util-tests.c
@@ -28,6 +28,8 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <stdlib.h>
+
 #include "util/util.h"
 #include "util/sss_utf8.h"
 #include "util/murmurhash3.h"
@@ -1020,6 +1022,46 @@ START_TEST(test_known_service)
 }
 END_TEST
 
+static void convert_time_tz(const char* tz)
+{
+    errno_t ret;
+    time_t unix_time;
+
+    ret = setenv("TZ", tz, 1);
+    fail_if(ret == -1);
+
+    ret = sss_utc_to_time_t("20140801115742Z", "%Y%m%d%H%M%SZ", &unix_time);
+    fail_unless(ret == EOK && difftime(1406894262, unix_time) == 0);
+}
+
+START_TEST(test_convert_time)
+{
+    const char *format = "%Y%m%d%H%M%SZ";
+    time_t unix_time;
+    errno_t ret;
+    const char *orig_tz;
+
+    ret = sss_utc_to_time_t("20150127133540P", format, &unix_time);
+    fail_unless(ret == ERR_INVALID_LOCK_TIME);
+    ret = sss_utc_to_time_t("0Z", format, &unix_time);
+    fail_unless(ret == EINVAL);
+    ret = sss_utc_to_time_t("000001010000Z", format, &unix_time);
+    fail_unless(ret == EINVAL);
+
+    /* test that results are still same no matter what timezone is set */
+    orig_tz = getenv("TZ");
+
+    convert_time_tz(orig_tz);
+
+    convert_time_tz("GST-1");
+
+    convert_time_tz("GST-2");
+
+    ret = setenv("TZ", orig_tz, 1);
+    fail_if(ret == -1);
+}
+END_TEST
+
 Suite *util_suite(void)
 {
     Suite *s = suite_create("util");
@@ -1067,10 +1109,17 @@ Suite *util_suite(void)
     tcase_add_test(tc_atomicio, test_atomicio_read_exact_sized_file);
     tcase_add_test(tc_atomicio, test_atomicio_read_from_empty_file);
 
+    TCase *tc_convert_time = tcase_create("convert_time");
+    tcase_add_checked_fixture(tc_convert_time,
+                              ck_leak_check_setup,
+                              ck_leak_check_teardown);
+    tcase_add_test(tc_convert_time, test_convert_time);
+
     suite_add_tcase (s, tc_util);
     suite_add_tcase (s, tc_utf8);
     suite_add_tcase (s, tc_mh3);
     suite_add_tcase (s, tc_atomicio);
+    suite_add_tcase (s, tc_convert_time);
 
     return s;
 }
diff --git a/src/util/util.c b/src/util/util.c
index 613c559bb2002686c7833642d0946e46e5a9b5d6..20cb473ae3f4e619a856118a3c02623d0ab0f6e1 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -18,6 +18,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include "config.h"
 #include <ctype.h>
 #include <netdb.h>
 #include <poll.h>
@@ -26,6 +27,7 @@
 #include <arpa/inet.h>
 #include <talloc.h>
 #include <dhash.h>
+#include <time.h>
 
 #include "util/util.h"
 #include "util/sss_utf8.h"
@@ -904,3 +906,54 @@ errno_t sss_fd_nonblocking(int fd)
 
     return EOK;
 }
+
+/* Convert GeneralizedTime (http://en.wikipedia.org/wiki/GeneralizedTime)
+ * to unix time (seconds since epoch). Use UTC time zone.
+ */
+errno_t sss_utc_to_time_t(const char *str, const char *format, time_t *_unix_time)
+{
+    char *end;
+    struct tm tm;
+    size_t len;
+    time_t ut;
+
+    if (str == NULL) {
+        return EINVAL;
+    }
+
+    len = strlen(str);
+    if (str[len-1] != 'Z') {
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              "%s does not seem to be in UTZ time zone.\n", str);
+        return ERR_INVALID_LOCK_TIME;
+    }
+
+    memset(&tm, 0, sizeof(tm));
+
+    end = strptime(str, format, &tm);
+    /* not all characters from format were matched */
+    if (end == NULL) {
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              "String [%s] failed to match format [%s].\n", str, format);
+        return EINVAL;
+    }
+
+    /* str is 'longer' than format */
+    if (*end != '\0') {
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              "String [%s] is longer than format [%s].\n", str, format);
+        return EINVAL;
+    }
+
+    ut = mktime(&tm);
+    if (ut == -1) {
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              "mktime failed to convert [%s].\n", str);
+        return EINVAL;
+    }
+
+    tzset();
+    ut -= timezone;
+    *_unix_time = ut;
+    return EOK;
+}
diff --git a/src/util/util.h b/src/util/util.h
index 22d6ef0a4e1340346d3d2997313aab50410f9dc0..829cf567ab24e907bf07082ae0a99e70e9668414 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -648,4 +648,7 @@ int set_seuser(const char *login_name, const char *seuser_name,
                const char *mlsrange);
 int del_seuser(const char *login_name);
 
+/* convert time from generalized form to unix time */
+errno_t sss_utc_to_time_t(const char *str, const char *format, time_t *unix_time);
+
 #endif /* __SSSD_UTIL_H__ */
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index 16d16fc777fc3344db8a3bdfeb3633bd5db48530..91f8c14e06701793d37d7586cf1099863edf4011 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -65,6 +65,7 @@ struct err_string error_to_str[] = {
     { "LDAP search returned a referral" }, /* ERR_REFERRAL */
     { "Error setting SELinux user context" }, /* ERR_SELINUX_CONTEXT */
     { "Username format not allowed by re_expression" }, /* ERR_REGEX_NOMATCH */
+    { "Invalid time of lockout" }, /* ERR_INVALID_LOCK_TIME */
 };
 
 
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 39455dc8adfe8784bd3f06382d701b7f9e97f004..4337ee17a054431013f420c00d8cc0bb7870f093 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -87,6 +87,7 @@ enum sssd_errors {
     ERR_REFERRAL,
     ERR_SELINUX_CONTEXT,
     ERR_REGEX_NOMATCH,
+    ERR_INVALID_LOCK_TIME,
     ERR_LAST            /* ALWAYS LAST */
 };
 
-- 
2.1.0

