>From c5fc4ea54bab0a9933148f4ef22a2d28cd5c71a7 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <preichl@redhat.com>
Date: Tue, 20 Jan 2015 18:34:44 -0500
Subject: [PATCH 3/3] SDAP: Lock out ssh keys when account naturally expires

Resolves:
https://fedorahosted.org/sssd/ticket/2534
---
 Makefile.am                      |   3 +-
 src/providers/ldap/sdap_access.c | 106 ++++++++++++++++++++++++++++++++++-----
 src/providers/ldap/sdap_access.h |   1 +
 3 files changed, 96 insertions(+), 14 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index ca8921bb132db6b7a57d9436a3219057d08b64c6..3bcfd6392fb88c7cf5869437e47ccfd8cabbfbc7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2382,7 +2382,8 @@ libsss_ldap_common_la_SOURCES = \
     src/providers/ldap/sdap_domain.c \
     src/providers/ldap/sdap.c \
     src/util/user_info_msg.c \
-    src/util/sss_ldap.c
+    src/util/sss_ldap.c \
+    src/util/util.c
 libsss_ldap_common_la_CFLAGS = \
     $(KRB5_CFLAGS)
 libsss_ldap_common_la_LIBADD = \
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index e09e6b8fe55e8e8bae9ff7f46e815595cb938971..39a6f4397f83b2ca560d0fb883002a97caf73c74 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -1568,7 +1568,9 @@ errno_t sdap_access_lock_step(struct tevent_req *req)
     errno_t ret;
     struct tevent_req *subreq;
     struct sdap_access_lock_req_ctx *state;
-    const char *attrs[] = { SYSDB_LDAP_ACCESS_LOCKED_TIME, NULL };
+    const char *attrs[] = { SYSDB_LDAP_ACCESS_LOCKED_TIME,
+                            SYSDB_LDAP_ACESS_LOCKOUT_DURATION,
+                            NULL };
 
     state = tevent_req_data(req, struct sdap_access_lock_req_ctx);
 
@@ -1597,15 +1599,86 @@ done:
     return ret;
 }
 
+static errno_t
+decide_if_account_is_locked(bool locked_only_by_admin,
+                            const char *pwdAccountLockedTime,
+                            const char *pwdAccountLockedDurationTime,
+                            bool *locked)
+{
+    errno_t ret;
+    time_t lock_time;
+    time_t duration;
+    time_t now;
+
+    /* Default action is to consider account to be locked. */
+    *locked = true;
+
+    /* account is permanently locked */
+    if (strcasecmp(pwdAccountLockedTime,
+                   PERMANENTLY_LOCKED_ACCOUNT) == 0) {
+        goto done;
+    }
+
+    if (locked_only_by_admin) {
+        /* We do *not* care about exact value of account locked time, we
+         * only *do* care if the value is equal to
+         * PERMANENTLY_LOCKED_ACCOUNT, which means that account is locked
+         * permanently.
+         */
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Account is beeing blocked by password policy, "
+              "but value: [%s] value is ignored by SSSD.\n",
+              pwdAccountLockedTime);
+        *locked = false;
+    } else {
+        /* Account may be locked out from natural reasons (too many attempts,
+         * expired password). In this case, pwdAccountLockedTime is also set,
+         * to the time of lock out.
+         */
+        ret = convert_time(pwdAccountLockedTime, "%Y%m%d%H%M%SZ",
+                           &lock_time);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_TRACE_FUNC, "convert_time() failed with %d:%s.\n",
+                  ret, sss_strerror(ret));
+            goto done;
+        }
+
+        now = time(NULL);
+
+        /* Account was NOT locked in past. */
+        if (difftime(lock_time, now) > 0.0) {
+            *locked = false;
+        } else if (pwdAccountLockedDurationTime != NULL) {
+            errno = 0;
+            duration = strtol(pwdAccountLockedDurationTime, NULL, 0);
+            if (errno) {
+                ret = errno;
+                goto done;
+            }
+            /* Lockout has expired */
+            if (duration != 0 && difftime(now, lock_time) > duration) {
+                *locked = false;
+            }
+        }
+    }
+
+    ret = EOK;
+
+done:
+    return ret;
+}
+
 static void sdap_access_lock_step_done(struct tevent_req *subreq)
 {
     int ret, tret, dp_error;
     size_t num_results;
     bool locked = false;
     const char *pwdAccountLockedTime;
+    const char *pwdAccountLockedDurationTime;
     struct sysdb_attrs **results;
     struct tevent_req *req;
     struct sdap_access_lock_req_ctx *state;
+    bool pwd_admin_locked_only;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct sdap_access_lock_req_ctx);
@@ -1613,6 +1686,9 @@ static void sdap_access_lock_step_done(struct tevent_req *subreq)
     ret = sdap_get_generic_recv(subreq, state, &num_results, &results);
     talloc_zfree(subreq);
 
+    pwd_admin_locked_only = dp_opt_get_bool(state->opts->basic,
+                                            SDAP_PWDLOCKOUT_ADMIN_LOCKED_ONLY);
+
     ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
     if (ret != EOK) {
         if (dp_error == DP_ERR_OK) {
@@ -1653,22 +1729,26 @@ static void sdap_access_lock_step_done(struct tevent_req *subreq)
         ret = ERR_INTERNAL;
         goto done;
     } else { /* Ok, we got a single reply */
+        ret = sysdb_attrs_get_string(results[0], SYSDB_LDAP_ACESS_LOCKOUT_DURATION,
+                                     &pwdAccountLockedDurationTime);
+        if (ret != EOK) {
+            /* This attribute might not be set even if account is locked */
+            pwdAccountLockedDurationTime = NULL;
+        }
+
         ret = sysdb_attrs_get_string(results[0], SYSDB_LDAP_ACCESS_LOCKED_TIME,
                                      &pwdAccountLockedTime);
         if (ret == EOK) {
-            /* We do *not* care about exact value of account locked time, we
-             * only *do* care if the value is equal to
-             * PERMANENTLY_LOCKED_ACCOUNT, which means that account is locked
-             * permanently.
-             */
-            if (strcasecmp(pwdAccountLockedTime,
-                           PERMANENTLY_LOCKED_ACCOUNT) == 0) {
+            ret = decide_if_account_is_locked(pwd_admin_locked_only,
+                                              pwdAccountLockedTime,
+                                              pwdAccountLockedDurationTime,
+                                              &locked);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_MINOR_FAILURE,
+                      "decide_if_account_is_locked failed: %d:[%s]. "
+                      "Account will be considered to be locked.\n",
+                      ret, sss_strerror(ret));
                 locked = true;
-            } else {
-                DEBUG(SSSDBG_TRACE_FUNC,
-                      "Account of: %s is beeing blocked by password policy, "
-                      "but value: [%s] value is ignored by SSSD.\n",
-                      state->username, pwdAccountLockedTime);
             }
         } else {
             /* Attribute SYSDB_LDAP_ACCESS_LOCKED_TIME in not be present unless
diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h
index f085e619961198b887d65ed5ee0bc5cdd90d1b20..3e59b9b2f4851a87c01f3e353c11b3bdf9bb4ff7 100644
--- a/src/providers/ldap/sdap_access.h
+++ b/src/providers/ldap/sdap_access.h
@@ -35,6 +35,7 @@
 #define SYSDB_LDAP_ACCESS_CACHED_LOCKOUT "ldap_access_lockout_allow"
 /* names of ppolicy attributes */
 #define SYSDB_LDAP_ACCESS_LOCKED_TIME "pwdAccountLockedTime"
+#define SYSDB_LDAP_ACESS_LOCKOUT_DURATION "pwdLockoutDuration"
 #define SYSDB_LDAP_ACCESS_LOCKOUT "pwdLockout"
 
 #define LDAP_ACCESS_FILTER_NAME "filter"
-- 
2.1.0

