----- Original Message -----
> On Thu, Jul 24, 2014 at 06:53:24AM -0400, Yassir Elley wrote:
> > Hi,
> > 
> > The attached patch adds support for an "entry_cache_gpo_timeout" option,
> > which allows an admin to specify how many seconds the backend should
> > consider locally stored gpo policy files to be valid before asking the
> > backend again. This is an additional performance enhancement (along with
> > comparing gpo versions to avoid unnecessary downloads). The timeout is set
> > when a gpo entry is stored in the cache (which only happens after policy
> > files have been downloaded).
> > 
> > During the processing of a gpo, if the gpo's cache timeout has not elapsed,
> > the backend does not interact with the gpo_child at all (and therefore
> > doesn't download any files from the smb server). Rather, it re-uses the
> > policy files that are already stored locally. Obviously, no gpo version
> > comparisons need to be made in this case. If the timeout has elapsed (or
> > there is no gpo entry in the cache), then the backend does interact with
> > the gpo child in the usual manner (including comparing gpo versions to
> > avoid unnecessary downloads). Note that the entry_cache_gpo_timeout option
> > is *not* an expiration for the data in the gpo cache entry (which includes
> > the cached_gpt_version). Indeed, the cached_gpt_version is never
> > considered to be "expired".
> > 
> > Note that this patch does not add support for offline mode, which will be
> > implemented in a subsequent patch.
> > 
> > Regards,
> > Yassir.
> 
> Hi,
> 
> I was wondering if it's a good idea to introduce the option as a global
> one, present in sssd.conf or a sssd-ad option? See for instance
> ipa_hbac_refresh in man sssd-ipa, I think that's quite natural
> counterpart.
> 
> Also, the default timeout is way too big, for access control, I think we
> should go with a lower timeout, the one used is for identity lookups
> only where we care more about speed than precision.
> 
> Some more comments inline:
> 
> > From 8aff4445df5cf42e09cd8b521cbd5ff4d6e1a666 Mon Sep 17 00:00:00 2001
> > From: Yassir Elley <[email protected]>
> > Date: Tue, 22 Jul 2014 14:19:35 -0400
> > Subject: [PATCH] AD-GPO: add entry_cache_gpo_timeout option
> > 
> > ---
> >  src/confdb/confdb.c                  |  11 ++
> >  src/confdb/confdb.h                  |   2 +
> >  src/config/SSSDConfig/__init__.py.in |   1 +
> >  src/config/SSSDConfigTest.py         |   2 +
> >  src/config/etc/sssd.api.conf         |   1 +
> >  src/db/sysdb.h                       |  14 +-
> >  src/db/sysdb_gpo.c                   |  46 +++++-
> >  src/man/sssd.conf.5.xml              |  13 ++
> 
> [...]
> 
> > @@ -1305,6 +1311,12 @@ ad_gpo_cse_step(struct tevent_req *req)
> >      struct tevent_req *subreq;
> >      struct ad_gpo_access_state *state;
> >      int i = 0;
> > +    struct ldb_result *res;
> > +    errno_t ret;
> > +    bool found_in_cache = false;
> > +    bool send_to_child = true;
> > +    int cached_gpt_version = 0;
> > +    time_t policy_file_timeout = 0;
> >  
> >      state = tevent_req_data(req, struct ad_gpo_access_state);
> >  
> > @@ -1327,14 +1339,69 @@ ad_gpo_cse_step(struct tevent_req *req)
> >      DEBUG(SSSDBG_TRACE_FUNC, "smb_path: %s\n",
> >      cse_filtered_gpo->smb_path);
> >      DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n",
> >      cse_filtered_gpo->gpo_guid);
> >  
> > +    /* retrieve gpo cache entry; set cached_gpt_version to -1 if
> > unavailable */
> > +    DEBUG(SSSDBG_TRACE_FUNC, "retrieving GPO from cache [%s]\n",
> > +          cse_filtered_gpo->gpo_guid);
> > +    ret = sysdb_gpo_get_gpo_by_guid(state,
> > +                                    state->domain,
> > +                                    cse_filtered_gpo->gpo_guid,
> > +                                    &res);
> > +    if (ret == EOK) {
> > +        found_in_cache = true;
> > +    } else {
> > +        switch (ret) {
> > +        case ENOENT:
> > +            DEBUG(SSSDBG_TRACE_FUNC, "ENOENT\n");
> > +            cached_gpt_version = -1;
> > +            break;
> > +        default:
> > +            DEBUG(SSSDBG_FATAL_FAILURE, "Could not read GPO from cache:
> > [%s]\n",
> > +                  strerror(ret));
> > +            return ret;
> > +        }
> > +    }
> 
> I think the above is too complex, what about:
> 
>      if (ret == EOK) {
>           /*
>            * Note: if the timeout is valid, then we can later avoid
>            downloading
>            * the GPT.INI file, as well as any policy files (i.e. we don't
>            need
>            * to interact with the gpo_child at all). However, even if the
>            timeout
>            * is not valid, while we will have to interact with the gpo child
>            to
>            * download the GPT.INI file, we may still be able to avoid
>            downloading
>            * the policy files (if the cached_gpt_version is the same as the
>            * GPT.INI version). In other words, the timeout is *not* an
>            expiration
>            * for the entire cache entry; the cached_gpt_version never
>            expires.
> _          */
>          cached_gpt_version = ldb_msg_find_attr_as_int(res->msgs[0],
>                                                         
> SYSDB_GPO_VERSION_ATTR,
>                                                         0);
>                                                                               
>                                                                               
>                             
>           policy_file_timeout = ldb_msg_find_attr_as_uint64
>               (res->msgs[0], SYSDB_GPO_TIMEOUT_ATTR, 0);
>                                                                               
>                                                                               
>                             
>           if (policy_file_timeout >= time(NULL)) {
>               send_to_child = false;
>           }
>      } else if (ret == ENOENT) {
>          DEBUG(SSSDBG_TRACE_FUNC, "ENOENT\n");
>          cached_gpt_version = -1;
>      } else {
>          DEBUG(SSSDBG_FATAL_FAILURE, "Could not read GPO from cache: [%s]\n",
>                strerror(ret));
>          return ret;
>       }
> 
> That way you don't need the found_in_cache variable at all.
> 
> > +
> > +    DEBUG(SSSDBG_TRACE_FUNC, "found_in_cache: %d\n", found_in_cache);
> > +    if (found_in_cache) {
> > +
> > +        /*
> > +         * Note: if the timeout is valid, then we can later avoid
> > downloading
> > +         * the GPT.INI file, as well as any policy files (i.e. we don't
> > need
> > +         * to interact with the gpo_child at all). However, even if the
> > timeout
> > +         * is not valid, while we will have to interact with the gpo child
> > to
> > +         * download the GPT.INI file, we may still be able to avoid
> > downloading
> > +         * the policy files (if the cached_gpt_version is the same as the
> > +         * GPT.INI version). In other words, the timeout is *not* an
> > expiration
> > +         * for the entire cache entry; the cached_gpt_version never
> > expires.
> > +         */
> > +
> > +        cached_gpt_version = ldb_msg_find_attr_as_int(res->msgs[0],
> > +
> > SYSDB_GPO_VERSION_ATTR,
> > +                                                      0);
> > +
> > +        policy_file_timeout = ldb_msg_find_attr_as_uint64
> > +            (res->msgs[0], SYSDB_GPO_TIMEOUT_ATTR, 0);
> > +
> > +        if (policy_file_timeout >= time(NULL)) {
> > +            send_to_child = false;
> > +        }
> > +    }
> > +
> > +    DEBUG(SSSDBG_TRACE_FUNC, "send_to_child: %d\n", send_to_child);
> > +    DEBUG(SSSDBG_TRACE_FUNC, "cached_gpt_version: %d\n",
> > cached_gpt_version);
> > +
> > +    cse_filtered_gpo->send_to_child = send_to_child;
> > +
> >      subreq = ad_gpo_process_cse_send(state,
> >                                       state->ev,
> > -                                     state->domain,
> > +                                     send_to_child,
> > +                                     state->domain->name,
> > +                                     cse_filtered_gpo->gpo_guid,
> >                                       cse_filtered_gpo->smb_server,
> >                                       cse_filtered_gpo->smb_share,
> >                                       cse_filtered_gpo->smb_path,
> >                                       GP_EXT_GUID_SECURITY_SUFFIX,
> > -                                     cse_filtered_gpo->gpo_guid);
> > +                                     cached_gpt_version);
> >  
> >      tevent_req_set_callback(subreq, ad_gpo_cse_done, req);
> >      return EAGAIN;
> > @@ -1356,10 +1423,12 @@ ad_gpo_cse_done(struct tevent_req *subreq)
> >      struct ad_gpo_access_state *state;
> >      int ret;
> >      int sysvol_gpt_version;
> > +    const char *policy_filename = NULL;
> >      char **allowed_sids;
> >      int allowed_size;
> >      char **denied_sids;
> >      int denied_size;
> > +    time_t now;
> >  
> >      req = tevent_req_callback_data(subreq, struct tevent_req);
> >      state = tevent_req_data(req, struct ad_gpo_access_state);
> > @@ -1372,8 +1441,7 @@ ad_gpo_cse_done(struct tevent_req *subreq)
> >      DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", gpo_guid);
> >  
> >      ret = ad_gpo_process_cse_recv(subreq, state, &sysvol_gpt_version,
> > -                                  &allowed_size, &allowed_sids,
> > -                                  &denied_size, &denied_sids);
> > +                                  &policy_filename);
> >  
> >      talloc_zfree(subreq);
> >  
> > @@ -1383,17 +1451,35 @@ ad_gpo_cse_done(struct tevent_req *subreq)
> >          goto done;
> >      }
> >  
> > -    DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
> > sysvol_gpt_version);
> > +    /* only store to cache (and refresh timeout) if we interacted with
> > child */
> > +    if (cse_filtered_gpo->send_to_child) {
> >  
> > -    ret = sysdb_gpo_store_gpo(state->domain, gpo_guid,
> > sysvol_gpt_version);
> > +        DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n",
> > sysvol_gpt_version);
> > +
> > +        now = time(NULL);
> > +        ret = sysdb_gpo_store_gpo(state->domain, gpo_guid,
> > sysvol_gpt_version,
> > +                                  state->domain->gpo_timeout, now);
> > +        if (ret != EOK) {
> > +            DEBUG(SSSDBG_OP_FAILURE, "Unable to store gpo cache entry:
> > [%d](%s}\n",
> > +                  ret, sss_strerror(ret));
> > +            goto done;
> > +        }
> > +    }
> 
> What about moving the above to the ad_gpo_process_cse* request? That way
> you could always store the GPO data after the child has finished and could
> avoid branching in the caller.
> 
> The rest looks good to me and the patches seem to work fine.

I agree with all your comments and have attached a revised patch. With regard 
to the timeout option, I have changed this to be an sssd-ad option. I have 
changed the name to ad_gpo_cache_timeout and have modelled it after 
ipa_hbac_refresh (including the 5 second default timeout).

Regards,
Yassir.
From 7e38602a15bb5d430cc3d5efcc91ef76d3a58a2e Mon Sep 17 00:00:00 2001
From: Yassir Elley <[email protected]>
Date: Tue, 22 Jul 2014 14:19:35 -0400
Subject: [PATCH] AD-GPO: add ad_gpo_cache_timeout option

---
 src/config/SSSDConfig/__init__.py.in   |   1 +
 src/config/etc/sssd.api.d/sssd-ad.conf |   1 +
 src/db/sysdb.h                         |  14 +-
 src/db/sysdb_gpo.c                     |  46 +++++-
 src/man/sssd-ad.5.xml                  |  16 ++
 src/providers/ad/ad_access.h           |   1 +
 src/providers/ad/ad_common.h           |   1 +
 src/providers/ad/ad_gpo.c              | 281 +++++++++++++++++++++------------
 src/providers/ad/ad_init.c             |   6 +
 src/providers/ad/ad_opts.h             |   1 +
 10 files changed, 260 insertions(+), 108 deletions(-)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 91f94b524b8185be96d701271443ee7a9ab67ee4..c3482b3a4df6d0fd39b2fa93c19ee5316dde0d94 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -169,6 +169,7 @@ option_strings = {
     'ad_access_filter' : _('LDAP filter to determine access privileges'),
     'ad_enable_gc' : _('Whether to use the Global Catalog for lookups'),
     'ad_gpo_access_control' : _('Operation mode for GPO-based access control'),
+    'ad_gpo_cache_timeout' : _("The amount of time between lookups of the GPO policy files against the AD server"),
 
     # [provider/krb5]
     'krb5_kdcip' : _('Kerberos server address'),
diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
index 7055c41be9ba3616459fa6683d310fa24c073a18..93d869c67f96afbea2e39d061daf578602b2f9a9 100644
--- a/src/config/etc/sssd.api.d/sssd-ad.conf
+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
@@ -7,6 +7,7 @@ ad_enable_dns_sites = bool, None, false
 ad_access_filter = str, None, false
 ad_enable_gc = bool, None, false
 ad_gpo_access_control = str, None, false
+ad_gpo_cache_timeout = int, None, false
 ldap_uri = str, None, false
 ldap_backup_uri = str, None, false
 ldap_search_base = str, None, false
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 63f596007cdf53c5ebbdccf37f3c4bcc7830a205..addf13196352825009a4a4a0ebaed7e343c72a14 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -873,6 +873,7 @@ errno_t sysdb_search_object_by_sid(TALLOC_CTX *mem_ctx,
 #define SYSDB_GPO_FILTER "(&(objectClass="SYSDB_GPO_OC")("SYSDB_GPO_GUID_ATTR"=%s))"
 #define SYSDB_GPO_GUID_ATTR "gpoGUID"
 #define SYSDB_GPO_VERSION_ATTR "gpoVersion"
+#define SYSDB_GPO_TIMEOUT_ATTR "gpoPolicyFileTimeout"
 
 #define SYSDB_TMPL_GPO_BASE SYSDB_GPO_CONTAINER","SYSDB_DOM_BASE
 #define SYSDB_TMPL_GPO SYSDB_GPO_GUID_ATTR"=%s,"SYSDB_TMPL_GPO_BASE
@@ -881,15 +882,18 @@ errno_t sysdb_search_object_by_sid(TALLOC_CTX *mem_ctx,
         SYSDB_NAME, \
         SYSDB_GPO_GUID_ATTR, \
         SYSDB_GPO_VERSION_ATTR, \
+        SYSDB_GPO_TIMEOUT_ATTR, \
         NULL }
 
 errno_t sysdb_gpo_store_gpo(struct sss_domain_info *domain,
                             const char *gpo_guid,
-                            int gpo_version);
+                            int gpo_version,
+                            int cache_timeout,
+                            time_t now);
 
-errno_t sysdb_gpo_get_gpo(TALLOC_CTX *mem_ctx,
-                          struct sss_domain_info *domain,
-                          const char *gpo_guid,
-                          struct ldb_result **_result);
+errno_t sysdb_gpo_get_gpo_by_guid(TALLOC_CTX *mem_ctx,
+                                  struct sss_domain_info *domain,
+                                  const char *gpo_guid,
+                                  struct ldb_result **_result);
 
 #endif /* __SYS_DB_H__ */
diff --git a/src/db/sysdb_gpo.c b/src/db/sysdb_gpo.c
index 3c23c5b8fae6aaa41168b1466f4b3e520231e7e2..228f131ace761909c98e0c02fffc889599f86c07 100644
--- a/src/db/sysdb_gpo.c
+++ b/src/db/sysdb_gpo.c
@@ -49,7 +49,9 @@ sysdb_gpo_dn(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain,
 errno_t
 sysdb_gpo_store_gpo(struct sss_domain_info *domain,
                     const char *gpo_guid,
-                    int gpo_version)
+                    int gpo_version,
+                    int cache_timeout,
+                    time_t now)
 {
     errno_t ret, sret;
     int lret;
@@ -81,6 +83,10 @@ sysdb_gpo_store_gpo(struct sss_domain_info *domain,
         goto done;
     }
 
+    if (!now) {
+        now = time(NULL);
+    }
+
     in_transaction = true;
 
     /* Check for an existing gpo_guid entry */
@@ -140,6 +146,21 @@ sysdb_gpo_store_gpo(struct sss_domain_info *domain,
             goto done;
         }
 
+        /* Add the Policy File Timeout */
+        lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_TIMEOUT_ATTR,
+                                 LDB_FLAG_MOD_ADD, NULL);
+        if (lret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(lret);
+            goto done;
+        }
+
+        lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_TIMEOUT_ATTR, "%lu",
+                               ((cache_timeout) ? (now + cache_timeout) : 0));
+        if (lret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(lret);
+            goto done;
+        }
+
         lret = ldb_add(domain->sysdb->ldb, update_msg);
         if (lret != LDB_SUCCESS) {
             DEBUG(SSSDBG_MINOR_FAILURE,
@@ -170,6 +191,21 @@ sysdb_gpo_store_gpo(struct sss_domain_info *domain,
             goto done;
         }
 
+        /* Add the Policy File Timeout */
+        lret = ldb_msg_add_empty(update_msg, SYSDB_GPO_TIMEOUT_ATTR,
+                                 LDB_FLAG_MOD_REPLACE, NULL);
+        if (lret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(lret);
+            goto done;
+        }
+
+        lret = ldb_msg_add_fmt(update_msg, SYSDB_GPO_TIMEOUT_ATTR, "%lu",
+                               ((cache_timeout) ? (now + cache_timeout) : 0));
+        if (lret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(lret);
+            goto done;
+        }
+
         lret = ldb_modify(domain->sysdb->ldb, update_msg);
         if (lret != LDB_SUCCESS) {
             DEBUG(SSSDBG_MINOR_FAILURE,
@@ -202,10 +238,10 @@ done:
 }
 
 errno_t
-sysdb_gpo_get_gpo(TALLOC_CTX *mem_ctx,
-                  struct sss_domain_info *domain,
-                  const char *gpo_guid,
-                  struct ldb_result **_result)
+sysdb_gpo_get_gpo_by_guid(TALLOC_CTX *mem_ctx,
+                          struct sss_domain_info *domain,
+                          const char *gpo_guid,
+                          struct ldb_result **_result)
 {
     errno_t ret;
     int lret;
diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
index d8c423541311922bb6d6eeec32df475a930fd1ba..8d90bc13359b025833b4badde50ec811241fe623 100644
--- a/src/man/sssd-ad.5.xml
+++ b/src/man/sssd-ad.5.xml
@@ -317,6 +317,22 @@ FOREST:EXAMPLE.COM:(memberOf=cn=admins,ou=groups,dc=example,dc=com)
                 </varlistentry>
 
                 <varlistentry>
+                    <term>ad_gpo_cache_timeout (integer)</term>
+                    <listitem>
+                        <para>
+                            The amount of time between lookups of GPO policy
+                            files against the AD server. This will reduce the
+                            latency and load on the AD server if there are
+                            many access-control requests made in a short
+                            period.
+                        </para>
+                        <para>
+                            Default: 5 (seconds)
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>dyndns_update (boolean)</term>
                     <listitem>
                         <para>
diff --git a/src/providers/ad/ad_access.h b/src/providers/ad/ad_access.h
index 4a9bd40a09472844fe9fa42f7453366264e627fd..60ff5db66c9663e736338cdf7694eb75584c3674 100644
--- a/src/providers/ad/ad_access.h
+++ b/src/providers/ad/ad_access.h
@@ -33,6 +33,7 @@ struct ad_access_ctx {
         GPO_ACCESS_CONTROL_PERMISSIVE,
         GPO_ACCESS_CONTROL_ENFORCING
     } gpo_access_control_mode;
+    int gpo_cache_timeout;
 };
 
 void
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index b2949c37fe68add7c44c25258aed45ce6d68c836..c3dc6c1a7362a312262d130ef6188f169bf9b832 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -51,6 +51,7 @@ enum ad_basic_opt {
     AD_ACCESS_FILTER,
     AD_ENABLE_GC,
     AD_GPO_ACCESS_CONTROL,
+    AD_GPO_CACHE_TIMEOUT,
 
     AD_OPTS_BASIC /* opts counter */
 };
diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
index a650fa13867e40bf8275a646885e229fa44bc57b..c26011d672886faadaf2bb60c4537c2de8bf6532 100644
--- a/src/providers/ad/ad_gpo.c
+++ b/src/providers/ad/ad_gpo.c
@@ -123,6 +123,7 @@ struct gp_gpo {
     int num_gpo_cse_guids;
     int gpo_func_version;
     int gpo_flags;
+    bool send_to_child;
 };
 
 enum ace_eval_status {
@@ -157,19 +158,19 @@ int ad_gpo_process_gpo_recv(struct tevent_req *req,
                             int *num_candidate_gpos);
 struct tevent_req *ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
                                            struct tevent_context *ev,
+                                           bool send_to_child,
                                            struct sss_domain_info *domain,
+                                           const char *gpo_guid,
                                            const char *smb_server,
                                            const char *smb_share,
                                            const char *smb_path,
                                            const char *smb_cse_suffix,
-                                           const char *gpo_guid);
+                                           int cached_gpt_version,
+                                           int gpo_timeout_option);
 int ad_gpo_process_cse_recv(struct tevent_req *req,
                             TALLOC_CTX *mem_ctx,
                             int *_sysvol_gpt_version,
-                            int *_allowed_size,
-                            char ***_allowed_sids,
-                            int *_denied_size,
-                            char ***_denied_sids);
+                            const char **_policy_filename);
 
 /* == ad_gpo_access_send/recv helpers =======================================*/
 
@@ -871,6 +872,7 @@ struct ad_gpo_access_state {
     struct sss_domain_info *domain;
     const char *user;
     enum gpo_access_control_mode gpo_mode;
+    int gpo_timeout_option;
     const char *ad_hostname;
     const char *target_dn;
     struct gp_gpo **dacl_filtered_gpos;
@@ -886,6 +888,12 @@ static void ad_gpo_process_som_done(struct tevent_req *subreq);
 static void ad_gpo_process_gpo_done(struct tevent_req *subreq);
 
 static errno_t ad_gpo_cse_step(struct tevent_req *req);
+static errno_t ad_gpo_parse_policy_file(TALLOC_CTX *mem_ctx,
+                                        const char *filename,
+                                        char ***allowed_sids,
+                                        int *allowed_size,
+                                        char ***denied_sids,
+                                        int *denied_size);
 static void ad_gpo_cse_done(struct tevent_req *subreq);
 
 struct tevent_req *
@@ -921,6 +929,7 @@ ad_gpo_access_send(TALLOC_CTX *mem_ctx,
     state->user = user;
     state->ldb_ctx = sysdb_ctx_get_ldb(domain->sysdb);
     state->gpo_mode = ctx->gpo_access_control_mode;
+    state->gpo_timeout_option = ctx->gpo_cache_timeout;
     state->ad_hostname = dp_opt_get_string(ctx->ad_options, AD_HOSTNAME);
     state->opts = ctx->sdap_access_ctx->id_ctx->opts;
     state->timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT);
@@ -1310,6 +1319,11 @@ ad_gpo_cse_step(struct tevent_req *req)
     struct tevent_req *subreq;
     struct ad_gpo_access_state *state;
     int i = 0;
+    struct ldb_result *res;
+    errno_t ret;
+    bool send_to_child = true;
+    int cached_gpt_version = 0;
+    time_t policy_file_timeout = 0;
 
     state = tevent_req_data(req, struct ad_gpo_access_state);
 
@@ -1332,14 +1346,60 @@ ad_gpo_cse_step(struct tevent_req *req)
     DEBUG(SSSDBG_TRACE_FUNC, "smb_path: %s\n", cse_filtered_gpo->smb_path);
     DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", cse_filtered_gpo->gpo_guid);
 
+    /* retrieve gpo cache entry; set cached_gpt_version to -1 if unavailable */
+    DEBUG(SSSDBG_TRACE_FUNC, "retrieving GPO from cache [%s]\n",
+          cse_filtered_gpo->gpo_guid);
+    ret = sysdb_gpo_get_gpo_by_guid(state,
+                                    state->domain,
+                                    cse_filtered_gpo->gpo_guid,
+                                    &res);
+    if (ret == EOK) {
+        /*
+         * Note: if the timeout is valid, then we can later avoid downloading
+         * the GPT.INI file, as well as any policy files (i.e. we don't need
+         * to interact with the gpo_child at all). However, even if the timeout
+         * is not valid, while we will have to interact with the gpo child to
+         * download the GPT.INI file, we may still be able to avoid downloading
+         * the policy files (if the cached_gpt_version is the same as the
+         * GPT.INI version). In other words, the timeout is *not* an expiration
+         * for the entire cache entry; the cached_gpt_version never expires.
+         */
+
+        cached_gpt_version = ldb_msg_find_attr_as_int(res->msgs[0],
+                                                      SYSDB_GPO_VERSION_ATTR,
+                                                      0);
+
+        policy_file_timeout = ldb_msg_find_attr_as_uint64
+            (res->msgs[0], SYSDB_GPO_TIMEOUT_ATTR, 0);
+
+        if (policy_file_timeout >= time(NULL)) {
+            send_to_child = false;
+        }
+    } else if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_FUNC, "ENOENT\n");
+        cached_gpt_version = -1;
+    } else {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Could not read GPO from cache: [%s]\n",
+               strerror(ret));
+        return ret;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "send_to_child: %d\n", send_to_child);
+    DEBUG(SSSDBG_TRACE_FUNC, "cached_gpt_version: %d\n", cached_gpt_version);
+
+    cse_filtered_gpo->send_to_child = send_to_child;
+
     subreq = ad_gpo_process_cse_send(state,
                                      state->ev,
+                                     send_to_child,
                                      state->domain,
+                                     cse_filtered_gpo->gpo_guid,
                                      cse_filtered_gpo->smb_server,
                                      cse_filtered_gpo->smb_share,
                                      cse_filtered_gpo->smb_path,
                                      GP_EXT_GUID_SECURITY_SUFFIX,
-                                     cse_filtered_gpo->gpo_guid);
+                                     cached_gpt_version,
+                                     state->gpo_timeout_option);
 
     tevent_req_set_callback(subreq, ad_gpo_cse_done, req);
     return EAGAIN;
@@ -1361,6 +1421,7 @@ ad_gpo_cse_done(struct tevent_req *subreq)
     struct ad_gpo_access_state *state;
     int ret;
     int sysvol_gpt_version;
+    const char *policy_filename = NULL;
     char **allowed_sids;
     int allowed_size;
     char **denied_sids;
@@ -1377,8 +1438,7 @@ ad_gpo_cse_done(struct tevent_req *subreq)
     DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", gpo_guid);
 
     ret = ad_gpo_process_cse_recv(subreq, state, &sysvol_gpt_version,
-                                  &allowed_size, &allowed_sids,
-                                  &denied_size, &denied_sids);
+                                  &policy_filename);
 
     talloc_zfree(subreq);
 
@@ -1388,17 +1448,20 @@ ad_gpo_cse_done(struct tevent_req *subreq)
         goto done;
     }
 
-    DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n", sysvol_gpt_version);
-
-    ret = sysdb_gpo_store_gpo(state->domain, gpo_guid, sysvol_gpt_version);
+    ret = ad_gpo_parse_policy_file(state,
+                                   policy_filename,
+                                   &allowed_sids,
+                                   &allowed_size,
+                                   &denied_sids,
+                                   &denied_size);
 
     if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Unable to store gpo cache entry: [%d](%s}\n",
-              ret, sss_strerror(ret));
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Cannot parse policy file: [%s][%d][%s]\n",
+              policy_filename, ret, strerror(ret));
         goto done;
     }
 
-    /* TBD: allowed/denied_sids/size, should be retrieved from cache */
     ret = ad_gpo_access_check
         (state, state->gpo_mode, state->user, state->domain,
          allowed_sids, allowed_size, denied_sids, denied_size);
@@ -2387,8 +2450,22 @@ ad_gpo_extract_smb_components(TALLOC_CTX *mem_ctx,
     *_smb_server = talloc_asprintf(mem_ctx, "%s%s",
                                    SMB_STANDARD_URI,
                                    server_hostname);
+    if (*_smb_server == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
     *_smb_share = talloc_asprintf(mem_ctx, "/%s", smb_share);
+    if (*_smb_share == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
     *_smb_path = talloc_asprintf(mem_ctx, "/%s", smb_path);
+    if (*_smb_path == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
 
     ret = EOK;
 
@@ -3141,8 +3218,13 @@ ad_gpo_parse_gpo_child_response(TALLOC_CTX *mem_ctx,
 
 struct ad_gpo_process_cse_state {
     struct tevent_context *ev;
+    struct sss_domain_info *domain;
+    int gpo_timeout_option;
+    const char *gpo_guid;
     const char *smb_path;
     const char *smb_cse_suffix;
+    int sysvol_gpt_version;
+    const char *policy_filename;
     pid_t child_pid;
     uint8_t *buf;
     ssize_t len;
@@ -3198,19 +3280,20 @@ static void gpo_cse_done(struct tevent_req *subreq);
 struct tevent_req *
 ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
                         struct tevent_context *ev,
+                        bool send_to_child,
                         struct sss_domain_info *domain,
+                        const char *gpo_guid,
                         const char *smb_server,
                         const char *smb_share,
                         const char *smb_path,
                         const char *smb_cse_suffix,
-                        const char *gpo_guid)
+                        int cached_gpt_version,
+                        int gpo_timeout_option)
 {
     struct tevent_req *req;
     struct tevent_req *subreq;
     struct ad_gpo_process_cse_state *state;
-    struct ldb_result *res;
     struct io_buffer *buf = NULL;
-    int cached_gpt_version = 0;
     errno_t ret;
 
     req = tevent_req_create(mem_ctx, &state, struct ad_gpo_process_cse_state);
@@ -3219,73 +3302,82 @@ ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
+    state->sysvol_gpt_version = -1;
+
+    if (!send_to_child) {
+        /*
+         * if we don't need to talk to child (b/c cache timeout is valid),
+         * we simply set the policy_filename and complete the request
+         */
+        state->policy_filename =
+            talloc_asprintf(state,
+                            GPO_CACHE_PATH"/%s/Policies/%s%s",
+                            domain->name,
+                            gpo_guid,
+                            GP_EXT_GUID_SECURITY_SUFFIX);
+        if (state->policy_filename == NULL) {
+            ret = ENOMEM;
+            goto immediately;
+        }
+
+        DEBUG(SSSDBG_TRACE_FUNC, "policy_filename:%s\n", state->policy_filename);
+        ret = EOK;
+        goto immediately;
+    }
+
     state->ev = ev;
     state->buf = NULL;
     state->len = 0;
+    state->domain = domain;
+    state->gpo_timeout_option = gpo_timeout_option;
+    state->gpo_guid = gpo_guid;
     state->smb_path = smb_path;
     state->smb_cse_suffix = smb_cse_suffix;
     state->io = talloc(state, struct io);
     if (state->io == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
         ret = ENOMEM;
-        goto fail;
+        goto immediately;
     }
 
     state->io->write_to_child_fd = -1;
     state->io->read_from_child_fd = -1;
     talloc_set_destructor((void *) state->io, gpo_child_io_destructor);
 
-    /* retrieve cached gpt version (or set it to -1 if unavailable) */
-    DEBUG(SSSDBG_TRACE_FUNC, "retrieving GPO from cache [%s]\n", gpo_guid);
-    ret = sysdb_gpo_get_gpo(state, domain, gpo_guid, &res);
-    if (ret != EOK) {
-        switch (ret) {
-        case ENOENT:
-            DEBUG(SSSDBG_TRACE_FUNC, "ENOENT\n");
-            cached_gpt_version = -1;
-            break;
-        default:
-            DEBUG(SSSDBG_FATAL_FAILURE, "Could not read GPO from cache: [%s]\n",
-                  strerror(ret));
-            goto fail;
-        }
-    }
-
-    if (cached_gpt_version != -1) {
-        /* cached_gpt_version is in cache, so retrieve it from cache */
-        cached_gpt_version = ldb_msg_find_attr_as_int(res->msgs[0],
-                                                      SYSDB_GPO_VERSION_ATTR,
-                                                      0);
-    }
-    DEBUG(SSSDBG_TRACE_FUNC, "cached_gpt_version: %d\n", cached_gpt_version);
-
     /* prepare the data to pass to child */
     ret = create_cse_send_buffer(state, smb_server, smb_share, smb_path,
                                  smb_cse_suffix, cached_gpt_version, &buf);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "create_cse_send_buffer failed.\n");
-        goto fail;
+        goto immediately;
     }
 
     ret = gpo_fork_child(req);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "gpo_fork_child failed.\n");
-        goto fail;
+        goto immediately;
     }
 
     subreq = write_pipe_send(state, ev, buf->data, buf->size,
                              state->io->write_to_child_fd);
     if (subreq == NULL) {
         ret = ENOMEM;
-        goto fail;
+        goto immediately;
     }
     tevent_req_set_callback(subreq, gpo_cse_step, req);
 
     return req;
 
-fail:
-    tevent_req_error(req, ret);
-    tevent_req_post(req, ev);
+immediately:
+
+    if (ret == EOK) {
+        tevent_req_done(req);
+        tevent_req_post(req, ev);
+    } else {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+
     return req;
 }
 
@@ -3321,6 +3413,9 @@ static void gpo_cse_done(struct tevent_req *subreq)
 {
     struct tevent_req *req;
     struct ad_gpo_process_cse_state *state;
+    uint32_t sysvol_gpt_version = -1;
+    uint32_t child_result;
+    time_t now;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ad_gpo_process_cse_state);
@@ -3336,69 +3431,59 @@ static void gpo_cse_done(struct tevent_req *subreq)
     close(state->io->read_from_child_fd);
     state->io->read_from_child_fd = -1;
 
-    tevent_req_done(req);
-    return;
-}
-
-int ad_gpo_process_cse_recv(struct tevent_req *req,
-                            TALLOC_CTX *mem_ctx,
-                            int *_sysvol_gpt_version,
-                            int *_allowed_size,
-                            char ***_allowed_sids,
-                            int *_denied_size,
-                            char ***_denied_sids)
-{
-    int ret;
-    uint32_t sysvol_gpt_version;
-    uint32_t result;
-    char **allowed_sids;
-    int allowed_size;
-    char **denied_sids;
-    int denied_size;
-    struct ad_gpo_process_cse_state *state;
-    const char *policy_filename = NULL;
-
-    state = tevent_req_data(req, struct ad_gpo_process_cse_state);
-
-    TEVENT_REQ_RETURN_ON_ERROR(req);
-
     ret = ad_gpo_parse_gpo_child_response(state, state->buf, state->len,
-                                          &sysvol_gpt_version, &result);
+                                          &sysvol_gpt_version, &child_result);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               "ad_gpo_parse_gpo_child_response failed: [%d][%s]\n",
               ret, strerror(ret));
-        return ret;
-    } else if (result != 0){
+        tevent_req_error(req, ret);
+    } else if (child_result != 0){
         DEBUG(SSSDBG_CRIT_FAILURE,
-              "Error in gpo_child: [%d][%s]\n", result, strerror(result));
-        return result;
+              "Error in gpo_child: [%d][%s]\n",
+              child_result, strerror(child_result));
+        tevent_req_error(req, child_result);
     }
 
-    policy_filename = talloc_asprintf(mem_ctx, GPO_CACHE_PATH"%s%s",
-                               state->smb_path, state->smb_cse_suffix);
-
-    DEBUG(SSSDBG_TRACE_FUNC, "policy_filename:%s\n", policy_filename);
+    state->sysvol_gpt_version = sysvol_gpt_version;
+    state->policy_filename = talloc_asprintf(state,
+                                             GPO_CACHE_PATH"%s%s",
+                                             state->smb_path,
+                                             state->smb_cse_suffix);
+    if (state->policy_filename == NULL) {
+        tevent_req_error(req, ENOMEM);
+    }
 
-    ret = ad_gpo_parse_policy_file(state,
-                                   policy_filename,
-                                   &allowed_sids,
-                                   &allowed_size,
-                                   &denied_sids,
-                                   &denied_size);
+    DEBUG(SSSDBG_TRACE_FUNC, "policy_filename:%s\n", state->policy_filename);
 
+    now = time(NULL);
+    DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n", sysvol_gpt_version);
+    ret = sysdb_gpo_store_gpo(state->domain, state->gpo_guid, sysvol_gpt_version,
+                              state->gpo_timeout_option, now);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Cannot parse policy file: [%s][%d][%s]\n",
-              policy_filename, ret, strerror(ret));
-        return ret;
+        DEBUG(SSSDBG_OP_FAILURE, "Unable to store gpo cache entry: [%d](%s}\n",
+              ret, sss_strerror(ret));
+        tevent_req_error(req, ret);
+        return;
     }
 
-    *_sysvol_gpt_version = sysvol_gpt_version;
-    *_allowed_size = allowed_size;
-    *_allowed_sids = talloc_steal(mem_ctx, allowed_sids);
-    *_denied_size = denied_size;
-    *_denied_sids = talloc_steal(mem_ctx, denied_sids);
+    tevent_req_done(req);
+    return;
+}
+
+int ad_gpo_process_cse_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            int *_sysvol_gpt_version,
+                            const char **_policy_filename)
+{
+    struct ad_gpo_process_cse_state *state;
+
+    state = tevent_req_data(req, struct ad_gpo_process_cse_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *_sysvol_gpt_version = state->sysvol_gpt_version;
+    *_policy_filename = talloc_steal(mem_ctx, state->policy_filename);
 
     return EOK;
 }
diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index 0a54d39709a61b39f74e2db04665c0911ab320ed..da3f6c4247f4599908c846d7be4e14b5c0e3d44d 100644
--- a/src/providers/ad/ad_init.c
+++ b/src/providers/ad/ad_init.c
@@ -371,6 +371,7 @@ sssm_ad_access_init(struct be_ctx *bectx,
     struct ad_id_ctx *ad_id_ctx;
     const char *filter;
     const char *gpo_access_control_mode;
+    int gpo_cache_timeout;
 
     access_ctx = talloc_zero(bectx, struct ad_access_ctx);
     if (!access_ctx) return ENOMEM;
@@ -439,6 +440,11 @@ sssm_ad_access_init(struct be_ctx *bectx,
         goto fail;
     }
 
+    /* GPO cache timeout */
+    gpo_cache_timeout =
+        dp_opt_get_int(access_ctx->ad_options, AD_GPO_CACHE_TIMEOUT);
+    access_ctx->gpo_cache_timeout = gpo_cache_timeout;
+
     *ops = &ad_access_ops;
     *pvt_data = access_ctx;
 
diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h
index 1f49f9c62247ae1cbed6ec5ec4c52ddb406ee1b6..a3ade012a7c1efb705a011697af36c28e39b1a9b 100644
--- a/src/providers/ad/ad_opts.h
+++ b/src/providers/ad/ad_opts.h
@@ -39,6 +39,7 @@ struct dp_option ad_basic_opts[] = {
     { "ad_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING},
     { "ad_enable_gc", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "ad_gpo_access_control", DP_OPT_STRING, { "permissive" }, NULL_STRING },
+    { "ad_gpo_cache_timeout", DP_OPT_NUMBER, { .number = 5 }, NULL_NUMBER },
     DP_OPTION_TERMINATOR
 };
 
-- 
1.8.5.3

_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to