From f5ab3f0135085ef85063734b7a03cb03b45b9f8c Mon Sep 17 00:00:00 2001
From: Yassir Elley <yelley@redhat.com>
Date: Wed, 21 May 2014 15:11:36 -0400
Subject: [PATCH] AD-GPO: Add initial gpo-smb implementation

---
 configure.ac                 |   1 +
 src/external/libsmbclient.m4 |   5 +
 src/providers/ad/ad_gpo.c    | 668 ++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 673 insertions(+), 1 deletion(-)
 create mode 100644 src/external/libsmbclient.m4

diff --git a/configure.ac b/configure.ac
index 58d3e9c474a06c7ae1ebb45385e3b8443a7f79ac..af76769ab2b0a28646c75637912ad5233290592d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -155,6 +155,7 @@ m4_include([src/external/inotify.m4])
 m4_include([src/external/samba.m4])
 m4_include([src/external/sasl.m4])
 m4_include([src/external/configlib.m4])
+m4_include([src/external/libsmbclient.m4])
 
 if test x$build_config_lib = xyes; then
     m4_include([src/external/libaugeas.m4])
diff --git a/src/external/libsmbclient.m4 b/src/external/libsmbclient.m4
new file mode 100644
index 0000000000000000000000000000000000000000..060b5ee149ee6a4c74e545ac9253f058ad9eb4bb
--- /dev/null
+++ b/src/external/libsmbclient.m4
@@ -0,0 +1,5 @@
+AC_SUBST(SMBCLIENT_CFLAGS)
+AC_SUBST(SMBCLIENT_LIBS)
+
+PKG_CHECK_MODULES(SMBCLIENT, smbclient, ,
+    AC_MSG_ERROR("Please install libsmbclient development libraries"))
diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
index 168ed2d7e8657799a7c773ec8b24897cdd303ec4..7cdc3a4989d7ae60a106416e6672e7d487dc2d54 100644
--- a/src/providers/ad/ad_gpo.c
+++ b/src/providers/ad/ad_gpo.c
@@ -31,6 +31,9 @@
  */
 
 #include <security/pam_modules.h>
+#include <libsmbclient.h>
+#include <ini_configobj.h>
+#include <ini_config.h>
 #include "util/util.h"
 #include "util/strtonum.h"
 #include "providers/data_provider.h"
@@ -47,6 +50,8 @@
 #include <ndr.h>
 #include <gen_ndr/security.h>
 
+/* == gpo-ldap constants =================================================== */
+
 #define AD_AT_DN "distinguishedName"
 #define AD_AT_UAC "userAccountControl"
 #define AD_AT_CONFIG_NC "configurationNamingContext"
@@ -66,6 +71,20 @@
 #define AD_AGP_GUID "edacfd8f-ffb3-11d1-b41d-00a0c968f939"
 #define AD_AUTHENTICATED_USERS_SID "S-1-5-11"
 
+/* == gpo-smb constants ==================================================== */
+
+#define GPO_VERSION_USER(x) (x >> 16)
+#define GPO_VERSION_MACHINE(x) (x & 0xffff)
+
+#define RIGHTS_SECTION "Privilege Rights"
+#define ALLOW_LOGON_LOCALLY "SeInteractiveLogonRight"
+#define DENY_LOGON_LOCALLY "SeDenyInteractiveLogonRight"
+
+#define SMB_BUFFER_SIZE 65536
+
+#define GP_EXT_GUID_SECURITY "{827D319E-6EAC-11D2-A4EA-00C04F79F83A}"
+#define GP_EXT_GUID_SECURITY_SUFFIX "/Microsoft/Windows NT/SecEdit/GptTmpl.inf"
+
 /* == common data structures and declarations ============================= */
 
 struct gp_som {
@@ -121,6 +140,16 @@ int ad_gpo_process_gpo_recv(struct tevent_req *req,
                             TALLOC_CTX *mem_ctx,
                             struct gp_gpo ***candidate_gpos,
                             int *num_candidate_gpos);
+struct tevent_req *ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           char *smb_uri,
+                                           struct gp_gpo *cse_filtered_gpo);
+int ad_gpo_process_cse_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            char ***allowed_sids,
+                            int *allowed_size,
+                            char ***denied_sids,
+                            int *denied_size);
 
 /* == ad_gpo_access_send/recv helpers =======================================*/
 
@@ -545,6 +574,240 @@ ad_gpo_filter_gpos_by_dacl(TALLOC_CTX *mem_ctx,
     return ret;
 }
 
+/*
+ * This function determines whether the input ACE includes any of the
+ * client's SIDs. The boolean result is assigned to the _included output param.
+ */
+static errno_t
+ad_gpo_includes_cse_guid(const char *cse_guid,
+                         const char **gpo_cse_guids,
+                         int num_gpo_cse_guids,
+                         bool *_included)
+{
+    int i = 0;
+    const char *gpo_cse_guid = NULL;
+
+    for (i = 0; i < num_gpo_cse_guids; i++) {
+        if (gpo_cse_guids[i] == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "null\n");
+        }
+        gpo_cse_guid = gpo_cse_guids[i];
+        if (strcmp(gpo_cse_guid, cse_guid) == 0) {
+            *_included = true;
+            return EOK;
+        }
+    }
+
+    *_included = false;
+    return EOK;
+}
+
+/*
+ * This function takes an input dacl_filtered_gpos list, filters out any gpo
+ * that does not contain the input cse_guid, and assigns the result to the
+ * _cse_filtered_gpos output parameter.
+ */
+static errno_t
+ad_gpo_filter_gpos_by_cse_guid(TALLOC_CTX *mem_ctx,
+                               const char *cse_guid,
+                               struct gp_gpo **dacl_filtered_gpos,
+                               int num_dacl_filtered_gpos,
+                               struct gp_gpo ***_cse_filtered_gpos,
+                               int *_num_cse_filtered_gpos)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    int i = 0;
+    int ret = 0;
+    struct gp_gpo *dacl_filtered_gpo = NULL;
+    int gpo_dn_idx = 0;
+    struct gp_gpo **cse_filtered_gpos = NULL;
+    bool included;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    cse_filtered_gpos = talloc_array(tmp_ctx,
+                                     struct gp_gpo *,
+                                     num_dacl_filtered_gpos + 1);
+    if (cse_filtered_gpos == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (i = 0; i < num_dacl_filtered_gpos; i++) {
+
+        dacl_filtered_gpo = dacl_filtered_gpos[i];
+        if (dacl_filtered_gpo == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "dacl_filtered_gpo is null\n");
+        }
+
+        DEBUG(SSSDBG_TRACE_ALL, "examining cse candidate_gpo_guid: %s\n",
+              dacl_filtered_gpo->gpo_guid);
+
+        if (dacl_filtered_gpo->gpo_cse_guids[0] == NULL) {
+            DEBUG(SSSDBG_TRACE_ALL, "dacl_filtered_gpo->gpo_cse_guids[0] is null\n");
+        }
+
+        ret = ad_gpo_includes_cse_guid(cse_guid,
+                                       dacl_filtered_gpo->gpo_cse_guids,
+                                       dacl_filtered_gpo->num_gpo_cse_guids,
+                                       &included);
+        if (included) {
+            DEBUG(SSSDBG_TRACE_ALL,
+                  "GPO applicable to target per cse_guid filtering\n");
+            cse_filtered_gpos[gpo_dn_idx] = talloc_steal(cse_filtered_gpos,
+                                                         dacl_filtered_gpo);
+            gpo_dn_idx++;
+        } else {
+            DEBUG(SSSDBG_TRACE_ALL,
+                  "GPO not applicable to target per cse_guid filtering\n");
+        }
+    }
+
+    cse_filtered_gpos[gpo_dn_idx] = NULL;
+
+    *_cse_filtered_gpos = talloc_steal(mem_ctx, cse_filtered_gpos);
+    *_num_cse_filtered_gpos = gpo_dn_idx;
+
+    ret = EOK;
+
+ done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+/*
+ * This cse-specific function (GP_EXT_GUID_SECURITY) populates the output found
+ * parameter based on whether the input user_sid or any of the input group_sids
+ * appear in the input list of privilege_sids.
+ */
+static void
+check_rights(char **privilege_sids,
+             int privilege_size,
+             const char *user_sid,
+             const char **group_sids,
+             int group_size,
+             int *found)
+{
+  int match = 0;
+  int i, j;
+
+  for (i = 0; i < privilege_size; i++) {
+    if (strcmp(user_sid, privilege_sids[i]) == 0) {
+      match = 1;
+      break;
+    }
+    for (j = 0; j < group_size; j++) {
+      if (strcmp(group_sids[j], privilege_sids[i]) == 0) {
+	match = 1;
+	break;
+      }
+    }
+  }
+
+  *found = match;
+}
+
+/*
+ * This cse-specific function (GP_EXT_GUID_SECURITY) performs HBAC policy
+ * application and determines whether logon access is granted or denied for
+ * the {user,domain} tuple specified in the inputs. This function returns EOK
+ * to indicate that access is granted. Any other return value indicates that
+ * access is denied.
+ *
+ * The access control algorithm first determines whether the "principal_sids"
+ * (i.e. user_sid or group_sids) appear in allowed_sids and denied_sids.
+ *
+ * For access to be granted, both the "allowed_sids_condition" *and* the
+ * "denied_sids_condition" must be met (in all other cases, access is denied).
+ * 1) The "allowed_sids_condition" is satisfied if any of the principal_sids
+ *    appears in allowed_sids OR if the allowed_sids list is empty
+ * 2) The "denied_sids_condition" is satisfied if none of the principal_sids
+ *    appear in denied_sids
+ *
+ * By default, neither allowed_sids nor denied_sids are specified. As such, a
+ * deployment that is unaware of GPO-based access-control policy settings is
+ * unaffected by them (b/c the absence of allowed_sids grants access).
+ *
+ * Note that if a principal_sid appears in both allowed_sids and denied_sids,
+ * the "allowed_sids_condition" is met, but the "denied_sids_condition" is not.
+ * In other words, Deny takes precedence over Allow.
+ */
+static errno_t
+ad_gpo_access_check(TALLOC_CTX *mem_ctx,
+                    const char *user,
+                    struct sss_domain_info *domain,
+                    char **allowed_sids,
+                    int allowed_size,
+                    char **denied_sids,
+                    int denied_size)
+{
+    const char *user_sid;
+    const char **group_sids;
+    int group_size = 0;
+    int access_granted = 0;
+    int access_denied = 0;
+    int ret;
+    int j;
+
+    DEBUG(SSSDBG_CRIT_FAILURE, "POLICY FILE:\n");
+    DEBUG(SSSDBG_CRIT_FAILURE, "allowed_size = %d\n", allowed_size);
+    for (j= 0; j < allowed_size; j++) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "allowed_sids[%d] = %s\n", j,
+              allowed_sids[j]);
+    }
+
+    DEBUG(SSSDBG_CRIT_FAILURE, "denied_size = %d\n", denied_size);
+    for (j= 0; j < denied_size; j++) {
+        DEBUG(SSSDBG_CRIT_FAILURE, " denied_sids[%d] = %s\n", j,
+              denied_sids[j]);
+    }
+
+    ret = ad_gpo_get_sids(mem_ctx, user, domain, &user_sid,
+                          &group_sids, &group_size);
+    if (ret) goto done;
+
+    DEBUG(SSSDBG_CRIT_FAILURE, "CURRENT USER:\n");
+    DEBUG(SSSDBG_CRIT_FAILURE, "       user_sid = %s\n", user_sid);
+
+    for (j= 0; j < group_size; j++) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "  group_sids[%d] = %s\n", j,
+              group_sids[j]);
+    }
+
+    /* If AllowLogonLocally is not defined, all users are allowed */
+    if (allowed_size == 0) {
+      access_granted = 1;
+    }  else {
+      check_rights(allowed_sids, allowed_size, user_sid,
+                   group_sids, group_size, &access_granted);
+    }
+
+    DEBUG(SSSDBG_CRIT_FAILURE, " access_granted = %d\n", access_granted);
+
+    check_rights(denied_sids, denied_size, user_sid,
+                 group_sids, group_size, &access_denied);
+    DEBUG(SSSDBG_CRIT_FAILURE, "  access_denied = %d\n", access_denied);
+
+    if (access_granted && !access_denied) {
+        return EOK;
+    } else {
+        return EACCES;
+    }
+
+ done:
+
+    if (ret) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Error encountered: %d.\n", ret);
+    }
+
+    return ret;
+}
+
+
 /* == ad_gpo_access_send/recv implementation ================================*/
 
 struct ad_gpo_access_state {
@@ -560,6 +823,9 @@ struct ad_gpo_access_state {
     const char *target_dn;
     struct gp_gpo **dacl_filtered_gpos;
     int num_dacl_filtered_gpos;
+    struct gp_gpo **cse_filtered_gpos;
+    int num_cse_filtered_gpos;
+    int cse_gpo_index;
 };
 
 static void ad_gpo_connect_done(struct tevent_req *subreq);
@@ -567,6 +833,9 @@ static void ad_gpo_target_dn_retrieval_done(struct tevent_req *subreq);
 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 void ad_gpo_cse_done(struct tevent_req *subreq);
+
 struct tevent_req *
 ad_gpo_access_send(TALLOC_CTX *mem_ctx,
                    struct tevent_context *ev,
@@ -588,6 +857,9 @@ ad_gpo_access_send(TALLOC_CTX *mem_ctx,
     state->domain = domain;
     state->dacl_filtered_gpos = NULL;
     state->num_dacl_filtered_gpos = 0;
+    state->cse_filtered_gpos = NULL;
+    state->num_cse_filtered_gpos = 0;
+    state->cse_gpo_index = -1;
     state->ev = ev;
     state->user = user;
     state->ldb_ctx = sysdb_ctx_get_ldb(domain->sysdb);
@@ -842,6 +1114,13 @@ ad_gpo_process_som_done(struct tevent_req *subreq)
 /*
  * This function retrieves a list of candidate_gpos and potentially reduces it
  * to a list of dacl_filtered_gpos, based on each GPO's DACL.
+ *
+ * This function then takes the list of dacl_filtered_gpos and potentially
+ * reduces it to a list of cse_filtered_gpos, based on whether each GPO's list
+ * of cse_guids includes the "SecuritySettings" CSE GUID (used for HBAC).
+ *
+ * This function then sends each cse_filtered_gpo to the CSE processing engine
+ * for policy application, which currently consists of HBAC functionality.
  */
 static void
 ad_gpo_process_gpo_done(struct tevent_req *subreq)
@@ -898,10 +1177,128 @@ ad_gpo_process_gpo_done(struct tevent_req *subreq)
               state->dacl_filtered_gpos[i]->gpo_guid);
     }
 
+    ret = ad_gpo_filter_gpos_by_cse_guid(state,
+                                         GP_EXT_GUID_SECURITY,
+                                         state->dacl_filtered_gpos,
+                                         state->num_dacl_filtered_gpos,
+                                         &state->cse_filtered_gpos,
+                                         &state->num_cse_filtered_gpos);
+
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to filter GPO list by CSE_GUID: [%d](%s)\n",
+               ret, strerror(ret));
+        goto done;
+    }
+
+    for (i = 0; i < state->num_cse_filtered_gpos; i++) {
+        DEBUG(SSSDBG_TRACE_FUNC, "cse_filtered_gpos[%d]->gpo_guid is %s\n", i,
+                                  state->cse_filtered_gpos[i]->gpo_guid);
+    }
+
     /* TBD: initiate SMB retrieval */
     DEBUG(SSSDBG_TRACE_FUNC, "time for SMB retrieval\n");
 
-    ret = EOK;
+    DEBUG(SSSDBG_TRACE_FUNC, "num_cse_filtered_gpos: %d\n",
+          state->num_cse_filtered_gpos);
+
+    ret = ad_gpo_cse_step(req);
+
+ done:
+
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+    }
+}
+
+static errno_t
+ad_gpo_cse_step(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct ad_gpo_access_state *state;
+    char *smb_uri;
+    int i = 0;
+
+    state = tevent_req_data(req, struct ad_gpo_access_state);
+
+    state->cse_gpo_index++;
+    struct gp_gpo *cse_filtered_gpo =
+        state->cse_filtered_gpos[state->cse_gpo_index];
+
+    /* gp_gpo is NULL only after all GPOs have been processed */
+    if (cse_filtered_gpo == NULL) return EOK;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "cse filtered_gpos[%d]->gpo_guid is %s\n",
+          state->cse_gpo_index, cse_filtered_gpo->gpo_guid);
+    DEBUG(SSSDBG_TRACE_FUNC, "cse filtered_gpos[%d]->file_sys_path is %s\n",
+          state->cse_gpo_index, cse_filtered_gpo->gpo_file_sys_path);
+    for (i = 0; i < cse_filtered_gpo->num_gpo_cse_guids; i++) {
+        DEBUG(SSSDBG_TRACE_ALL,
+              "cse_filtered_gpos[%d]->gpo_cse_guids[%d]->gpo_guid is %s\n",
+              state->cse_gpo_index, i, cse_filtered_gpo->gpo_cse_guids[i]);
+    }
+
+    smb_uri = talloc_asprintf(state, "%s%s",
+                              cse_filtered_gpo->gpo_file_sys_path,
+                              GP_EXT_GUID_SECURITY_SUFFIX);
+
+    subreq = ad_gpo_process_cse_send(state, state->ev, smb_uri, cse_filtered_gpo);
+
+    tevent_req_set_callback(subreq, ad_gpo_cse_done, req);
+    return EAGAIN;
+}
+
+/*
+ * This cse-specific function (GP_EXT_GUID_SECURITY) retrieves a list of
+ * allowed_sids and denied_sids, and uses them to determine whether logon
+ * access is granted or denied for the state's {user, domain} tuple.
+ *
+ * If it is determined that the current cse_filtered_gpo grants access, then
+ * we process the next cse_filtered_gpo in the list. At any time, if access is
+ * denied, we return immediately with an error.
+ */
+static void
+ad_gpo_cse_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct ad_gpo_access_state *state;
+    int ret;
+    char **allowed_sids;
+    int allowed_size;
+    char **denied_sids;
+    int denied_size;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ad_gpo_access_state);
+
+    ret = ad_gpo_process_cse_recv(subreq, state, &allowed_sids, &allowed_size,
+                                  &denied_sids, &denied_size);
+
+    talloc_zfree(subreq);
+
+    if (ret != EOK) {
+        /* TBD: handle ret error  */
+        goto done;
+    }
+
+    /* TBD: allowed/denied_sids/size, should be retrieved from cache */
+    ret = ad_gpo_access_check
+        (state, state->user, state->domain,
+         allowed_sids, allowed_size, denied_sids, denied_size);
+
+    if (ret == EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "ret is EOK\n");
+    } else {
+        DEBUG(SSSDBG_CRIT_FAILURE, "ret is not EOK\n");
+        DEBUG(SSSDBG_OP_FAILURE,
+              "GPO check failed: [%d](%s)\n",
+              ret, strerror(ret));
+        goto done;
+    }
+
+    ret = ad_gpo_cse_step(req);
 
  done:
 
@@ -2298,3 +2695,272 @@ ad_gpo_process_gpo_recv(struct tevent_req *req,
     *num_candidate_gpos = state->num_candidate_gpos;
     return EOK;
 }
+
+/* == ad_gpo_process_cse_send/recv helpers ================================= */
+
+static errno_t
+parse_logon_right_with_libini(struct ini_cfgobj *ini_config,
+                              const char *section,
+                              const char *name,
+                              int *_size,
+                              char ***_sids)
+{
+  int ret = 0;
+  struct value_obj *vobj = NULL;
+  char **sids = NULL;
+  int num_sids;
+  int i;
+
+  ret = ini_get_config_valueobj(section, name, ini_config, INI_GET_FIRST_VALUE,
+                                &vobj);
+  if (vobj == NULL) {
+      DEBUG(SSSDBG_CRIT_FAILURE, "section/name not found: [%s][%s]\n",
+            section, name);
+      return EOK;
+  }
+  sids = ini_get_string_config_array(vobj, NULL, &num_sids, &ret);
+
+  if (ret) {
+      DEBUG(SSSDBG_CRIT_FAILURE,
+            "ini_get_string_config_array failed [%d][%s]\n", ret, strerror(ret));
+      return ret;
+  }
+
+  for (i = 0; i <num_sids; i++) {
+    if (sids[i][0] == '*') {
+      sids[i]++;
+    }
+  }
+
+  *_size = num_sids;
+  *_sids = sids;
+
+  return EOK;
+}
+
+static errno_t
+ad_gpo_parse_security_cse_buffer(uint8_t *data_buf,
+                                 int data_len,
+                                 char ***allowed_sids,
+                                 int *allowed_size,
+                                 char ***denied_sids,
+                                 int *denied_size)
+{
+    struct ini_cfgfile *file_ctx = NULL;
+    struct ini_cfgobj *ini_config = NULL;
+    int ret;
+    char **allow_sids = NULL; char **deny_sids = NULL;
+    int allow_size = 0; int deny_size = 0;
+
+    ret = ini_config_create(&ini_config);
+    if (ret) goto done;
+    ret = ini_config_file_from_mem(data_buf, data_len, &file_ctx);
+    if (ret) goto done;
+    ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, 0, 0, ini_config);
+    if (ret) goto done;
+
+    ret = parse_logon_right_with_libini(ini_config,
+                                        RIGHTS_SECTION,
+                                        ALLOW_LOGON_LOCALLY,
+                                        &allow_size,
+                                        &allow_sids);
+    if (ret) {goto done;}
+
+    ret = parse_logon_right_with_libini(ini_config,
+                                        RIGHTS_SECTION,
+                                        DENY_LOGON_LOCALLY,
+                                        &deny_size,
+                                        &deny_sids);
+    if (ret) {goto done;}
+
+    *allowed_sids = allow_sids;
+    *allowed_size = allow_size;
+    *denied_sids = deny_sids;
+    *denied_size = deny_size;
+
+ done:
+
+    if (ret) {
+      DEBUG(SSSDBG_CRIT_FAILURE, "Error encountered: %d.\n", ret);
+    }
+
+    ini_config_file_close(file_ctx);
+    return ret;
+}
+
+static void
+sssd_krb_get_auth_data_fn(const char * pServer,
+                     const char * pShare,
+                     char * pWorkgroup,
+                     int maxLenWorkgroup,
+                     char * pUsername,
+                     int maxLenUsername,
+                     char * pPassword,
+                     int maxLenPassword)
+{
+    /* since we are using kerberos for authentication, we simply return */
+    return;
+}
+
+
+/*
+ * This cse-specific function (GP_EXT_GUID_SECURITY) opens an SMB connection,
+ * retrieves the data referenced by the input smb_uri, and then closes the SMB
+ * connection. The data is then parsed and the results are used to populate the
+ * output parameters with the list of allowed_sids and denied_sids
+ */
+static errno_t
+process_security_settings_cse(TALLOC_CTX *mem_ctx,
+                              const char *smb_uri,
+                              char ***_allowed_sids,
+                              int *_allowed_size,
+                              char ***_denied_sids,
+                              int *_denied_size)
+{
+    SMBCCTX *context;
+    int ret = 0;
+    uint8_t *buf = NULL;
+    int bytesread = 0;
+
+    char **allowed_sids;
+    char **denied_sids;
+    int allowed_size = 0;
+    int denied_size = 0;
+
+    DEBUG(SSSDBG_TRACE_ALL, "%s\n", smb_uri);
+
+    context = smbc_new_context();
+    if (!context) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate new smbc context\n");
+    }
+
+    smbc_setFunctionAuthData(context, sssd_krb_get_auth_data_fn);
+    smbc_setOptionUseKerberos(context, 1);
+    /* TBD: figure out whether we need the following function call */
+    /* smbc_setOptionFallbackAfterKerberos(context, 1); */
+
+    /* Initialize the context using the previously specified options */
+    if (!smbc_init_context(context)) {
+        smbc_free_context(context, 0);
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize smbc context\n");
+    }
+
+    /* Tell the compatibility layer to use this context */
+    smbc_set_context(context);
+
+    int remotehandle = smbc_open(smb_uri, O_RDONLY, 0755);
+    if (remotehandle < 0) DEBUG(SSSDBG_CRIT_FAILURE, "smbc_open failed\n");
+
+    buf = talloc_array(mem_ctx, uint8_t, SMB_BUFFER_SIZE);
+    bytesread = smbc_read(remotehandle, buf, SMB_BUFFER_SIZE);
+    if(bytesread < 0) DEBUG(SSSDBG_CRIT_FAILURE, "smbc_read failed\n");
+
+    DEBUG(SSSDBG_CRIT_FAILURE, "bytesread: %d\n", bytesread);
+
+    smbc_close(remotehandle);
+
+    ret = ad_gpo_parse_security_cse_buffer(buf,
+                                           bytesread,
+                                           &allowed_sids,
+                                           &allowed_size,
+                                           &denied_sids,
+                                           &denied_size);
+
+    /* TBD: allowed/denied_sids/size should be stored in cache */
+
+    *_allowed_sids = allowed_sids;
+    *_allowed_size = allowed_size;
+    *_denied_sids = denied_sids;
+    *_denied_size = denied_size;
+
+    return ret;
+}
+
+/* == ad_gpo_process_cse_send/recv implementation ========================== */
+
+struct ad_gpo_process_cse_state {
+    struct tevent_context *ev;
+    char **allowed_sids;
+    int allowed_size;
+    char **denied_sids;
+    int denied_size;
+    struct gp_gpo *cse_filtered_gpo;
+};
+
+/*
+ * This cse-specific function (GP_EXT_GUID_SECURITY) retrieves the data
+ * referenced by the input smb_uri, and uses the parsed results to populate the
+ * state's list of allowed_sids and denied_sids.
+ */
+struct tevent_req *
+ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
+                        struct tevent_context *ev,
+                        char *smb_uri,
+                        struct gp_gpo *cse_filtered_gpo)
+{
+    struct tevent_req *req;
+    struct ad_gpo_process_cse_state *state;
+    errno_t ret;
+
+    char **allowed_sids;
+    int allowed_size;
+    char **denied_sids;
+    int denied_size;
+
+    req = tevent_req_create(mem_ctx, &state, struct ad_gpo_process_cse_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->cse_filtered_gpo = cse_filtered_gpo;
+
+    ret = process_security_settings_cse
+        (state, smb_uri, &allowed_sids, &allowed_size,
+         &denied_sids, &denied_size);
+
+   if (ret != EOK) {
+       DEBUG(SSSDBG_CRIT_FAILURE, "process_security_settings_cse FAILED\n");
+       goto done;
+    }
+
+   state->allowed_sids = allowed_sids;
+   state->allowed_size = allowed_size;
+   state->denied_sids = denied_sids;
+   state->denied_size = denied_size;
+
+   ret = EOK;
+
+ done:
+
+    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;
+}
+
+int ad_gpo_process_cse_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            char ***allowed_sids,
+                            int *allowed_size,
+                            char ***denied_sids,
+                            int *denied_size)
+{
+    struct ad_gpo_process_cse_state *state =
+        tevent_req_data(req, struct ad_gpo_process_cse_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *allowed_sids = state->allowed_sids;
+    *allowed_size = state->allowed_size;
+    *denied_sids = state->denied_sids;
+    *denied_size = state->denied_size;
+
+    return EOK;
+}
-- 
1.8.5.3

