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

---
 Makefile.am                     |  22 +
 contrib/sssd.spec.in            |   2 +-
 src/external/samba.m4           |   8 +
 src/providers/ad/ad_gpo.c       | 874 +++++++++++++++++++++++++++++++++++++++-
 src/providers/ad/ad_gpo_child.c | 630 +++++++++++++++++++++++++++++
 5 files changed, 1523 insertions(+), 13 deletions(-)
 create mode 100644 src/providers/ad/ad_gpo_child.c

diff --git a/Makefile.am b/Makefile.am
index f64c77a5bdd7f53b41bd37572b2d91434a9893a6..2c04de81333ad197b7e42946475c527ad4c1e309 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -120,6 +120,9 @@ endif
 if BUILD_IFP
 sssdlibexec_PROGRAMS += sssd_ifp
 endif
+if BUILD_SAMBA
+sssdlibexec_PROGRAMS += gpo_child
+endif
 
 
 if BUILD_PAC_RESPONDER
@@ -2294,6 +2297,25 @@ ldap_child_LDADD = \
     $(DHASH_LIBS) \
     $(KRB5_LIBS)
 
+gpo_child_SOURCES = \
+    src/providers/ad/ad_gpo_child.c \
+    src/util/atomic_io.c \
+    src/util/util.c \
+    src/util/signal.c
+gpo_child_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(POPT_CFLAGS) \
+    $(KRB5_CFLAGS) \
+    $(INI_CONFIG_CFLAGS) \
+    $(SMBCLIENT_CFLAGS)
+gpo_child_LDADD = \
+    libsss_debug.la \
+    $(TALLOC_LIBS) \
+    $(POPT_LIBS) \
+    $(DHASH_LIBS) \
+    $(INI_CONFIG_LIBS) \
+    $(SMBCLIENT_LIBS)
+
 proxy_child_SOURCES = \
     src/providers/proxy/proxy_child.c \
     src/providers/data_provider_iface_generated.c \
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index b5e7903713ccc6944fbed4f84c2f0413a257b760..10704ae48cf5636de2a5cbbb42fc00b99052144f 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -97,7 +97,7 @@ BuildRequires: libtdb-devel
 BuildRequires: libldb-devel
 BuildRequires: libdhash-devel >= 0.4.2
 BuildRequires: libcollection-devel
-BuildRequires: libini_config-devel
+BuildRequires: libini_config-devel >= 1.1
 BuildRequires: dbus-devel
 BuildRequires: dbus-libs
 %if 0%{?rhel5_minor} >= 7
diff --git a/src/external/samba.m4 b/src/external/samba.m4
index 735cc5a187dba7514a9c7436a6d3ba948ad522b0..bf790365468da5ccdd40a260d74893490b7e7be8 100644
--- a/src/external/samba.m4
+++ b/src/external/samba.m4
@@ -19,4 +19,12 @@ If you do not want to build these providers it is possible to build SSSD
 without them. In this case, you will need to execute configure script
 with argument --without-samba
     ]]))
+
+    PKG_CHECK_MODULES(INI_CONFIG, ini_config >= 1.0.0, ,
+        AC_MSG_ERROR([[Please install libini_config development libraries.
+libini_config libraries are necessary for building ad and ipa provider.
+If you do not want to build these providers it is possible to build SSSD
+without them. In this case, you will need to execute configure script
+with argument --without-samba
+    ]]))
 fi
diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
index bb6896e70ae96036b1a9e58a6c8e25d3d8f1543f..5a5e057497b923c8b565c9af2b8b94358e44fc2c 100644
--- a/src/providers/ad/ad_gpo.c
+++ b/src/providers/ad/ad_gpo.c
@@ -28,11 +28,13 @@
  * are used by the public functions):
  *   ad_gpo_process_som_send/recv: populate list of gp_som objects
  *   ad_gpo_process_gpo_send/recv: populate list of gp_gpo objects
+ *   ad_gpo_process_cse_send/recv: retrieve policy file data
  */
 
 #include <security/pam_modules.h>
 #include "util/util.h"
 #include "util/strtonum.h"
+#include "util/child_common.h"
 #include "providers/data_provider.h"
 #include "providers/dp_backend.h"
 #include "providers/ad/ad_access.h"
@@ -47,6 +49,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 +70,25 @@
 #define AD_AGP_GUID "edacfd8f-ffb3-11d1-b41d-00a0c968f939"
 #define AD_AUTHENTICATED_USERS_SID "S-1-5-11"
 
+/* == gpo-smb constants ==================================================== */
+
+#define SMB_STANDARD_URI "smb://"
+
+#define GPO_VERSION_USER(x) (x >> 16)
+#define GPO_VERSION_MACHINE(x) (x & 0xffff)
+
+#define GP_EXT_GUID_SECURITY "{827D319E-6EAC-11D2-A4EA-00C04F79F83A}"
+#define GP_EXT_GUID_SECURITY_SUFFIX "/Microsoft/Windows NT/SecEdit/GptTmpl.inf"
+
+#ifndef SSSD_LIBEXEC_PATH
+#error "SSSD_LIBEXEC_PATH not defined"
+#else
+#define GPO_CHILD SSSD_LIBEXEC_PATH"/gpo_child"
+#endif
+
+/* fd used by the gpo_child process for logging */
+int gpo_child_debug_fd = -1;
+
 /* == common data structures and declarations ============================= */
 
 struct gp_som {
@@ -115,12 +138,22 @@ struct tevent_req *ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
                                            struct tevent_context *ev,
                                            struct sdap_id_op *sdap_op,
                                            struct sdap_options *opts,
+                                           char *server_hostname,
                                            int timeout,
                                            struct gp_som **som_list);
 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);
+int ad_gpo_process_cse_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            int *_allowed_size,
+                            char ***_allowed_sids,
+                            int *_denied_size,
+                            char ***_denied_sids);
 
 /* == ad_gpo_access_send/recv helpers =======================================*/
 
@@ -543,6 +576,254 @@ ad_gpo_filter_gpos_by_dacl(TALLOC_CTX *mem_ctx,
     return ret;
 }
 
+/*
+ * This function determines whether the input cse_guid matches any of the input
+ * gpo_cse_guids. The boolean result is assigned to the _included output param.
+ */
+static bool
+ad_gpo_includes_cse_guid(const char *cse_guid,
+                         const char **gpo_cse_guids,
+                         int num_gpo_cse_guids)
+{
+    int i = 0;
+    const char *gpo_cse_guid = NULL;
+
+    for (i = 0; i < num_gpo_cse_guids; i++) {
+        gpo_cse_guid = gpo_cse_guids[i];
+        if (strcmp(gpo_cse_guid, cse_guid) == 0) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/*
+ * 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];
+
+        DEBUG(SSSDBG_TRACE_ALL, "examining cse candidate_gpo_guid: %s\n",
+              dacl_filtered_gpo->gpo_guid);
+
+        included = ad_gpo_includes_cse_guid(cse_guid,
+                                            dacl_filtered_gpo->gpo_cse_guids,
+                                            dacl_filtered_gpo->num_gpo_cse_guids);
+
+        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);
+            dacl_filtered_gpos[i] = NULL;
+            gpo_dn_idx++;
+        } else {
+            DEBUG(SSSDBG_TRACE_ALL,
+                  "GPO not applicable to target per cse_guid filtering\n");
+            continue;
+        }
+    }
+
+    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
+ * parameter (found) based on whether the input user_sid or any of the input
+ * group_sids appear in the input list of privilege_sids.
+ */
+static bool
+check_rights(char **privilege_sids,
+             int privilege_size,
+             const char *user_sid,
+             const char **group_sids,
+             int group_size)
+{
+    int i, j;
+
+    for (i = 0; i < privilege_size; i++) {
+        if (strcmp(user_sid, privilege_sids[i]) == 0) {
+            return true;
+        }
+        for (j = 0; j < group_size; j++) {
+            if (strcmp(group_sids[j], privilege_sids[i]) == 0) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+/*
+ * 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
+ *
+ * Note that 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;
+    bool access_granted = false;
+    bool access_denied = false;
+    int ret;
+    int j;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "POLICY FILE:\n");
+    DEBUG(SSSDBG_TRACE_FUNC, "allowed_size = %d\n", allowed_size);
+    for (j= 0; j < allowed_size; j++) {
+        DEBUG(SSSDBG_TRACE_FUNC, "allowed_sids[%d] = %s\n", j,
+              allowed_sids[j]);
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "denied_size = %d\n", denied_size);
+    for (j= 0; j < denied_size; j++) {
+        DEBUG(SSSDBG_TRACE_FUNC, " 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 != EOK) {
+        ret = ERR_NO_SIDS;
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to retrieve SIDs: [%d](%s)\n", ret, sss_strerror(ret));
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "CURRENT USER:\n");
+    DEBUG(SSSDBG_TRACE_FUNC, "       user_sid = %s\n", user_sid);
+
+    for (j= 0; j < group_size; j++) {
+        DEBUG(SSSDBG_TRACE_FUNC, "  group_sids[%d] = %s\n", j,
+              group_sids[j]);
+    }
+
+    /* If AllowLogonLocally is not defined, all users are allowed */
+    if (allowed_size == 0) {
+        access_granted = true;
+    }  else {
+        access_granted = check_rights(allowed_sids, allowed_size, user_sid,
+                                      group_sids, group_size);
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, " access_granted = %d\n", access_granted);
+
+    access_denied = check_rights(denied_sids, denied_size, user_sid,
+                                 group_sids, group_size);
+    DEBUG(SSSDBG_TRACE_FUNC, "  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;
+}
+
+#define GPO_CHILD_LOG_FILE "gpo_child"
+static errno_t gpo_child_init(void)
+{
+    int ret;
+    FILE *debug_filep;
+
+    if (debug_to_file != 0 && gpo_child_debug_fd == -1) {
+        ret = open_debug_file_ex(GPO_CHILD_LOG_FILE, &debug_filep, false);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n",
+                        ret, strerror(ret));
+            return ret;
+        }
+
+        gpo_child_debug_fd = fileno(debug_filep);
+        if (gpo_child_debug_fd == -1) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                  "fileno failed [%d][%s]\n", errno, strerror(errno));
+            ret = errno;
+            return ret;
+        }
+    }
+
+    return EOK;
+}
+
 /* == ad_gpo_access_send/recv implementation ================================*/
 
 struct ad_gpo_access_state {
@@ -550,6 +831,7 @@ struct ad_gpo_access_state {
     struct ldb_context *ldb_ctx;
     struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *sdap_op;
+    char *server_hostname;
     struct sdap_options *opts;
     int timeout;
     struct sss_domain_info *domain;
@@ -558,6 +840,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);
@@ -565,6 +850,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,
@@ -575,8 +863,12 @@ ad_gpo_access_send(TALLOC_CTX *mem_ctx,
     struct tevent_req *req;
     struct tevent_req *subreq;
     struct ad_gpo_access_state *state;
+    char *server_uri;
     errno_t ret;
 
+    /* setup logging for gpo child */
+    gpo_child_init();
+
     req = tevent_req_create(mem_ctx, &state, struct ad_gpo_access_state);
     if (req == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
@@ -586,6 +878,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);
@@ -600,6 +895,19 @@ ad_gpo_access_send(TALLOC_CTX *mem_ctx,
         goto immediately;
     }
 
+    /* extract server_hostname from server_uri */
+    server_uri = state->conn->service->uri;
+    if (strncasecmp(server_uri, LDAP_STANDARD_URI, strlen(LDAP_STANDARD_URI)) == 0) {
+        state->server_hostname = server_uri + strlen(LDAP_STANDARD_URI);
+    } else if (strncasecmp(server_uri, LDAP_SSL_URI, strlen(LDAP_SSL_URI)) == 0) {
+        state->server_hostname = server_uri + strlen(LDAP_SSL_URI);
+    } else {
+        ret = EINVAL;
+        goto immediately;
+    }
+    DEBUG(SSSDBG_TRACE_ALL, "server_hostname from uri: %s\n",
+          state->server_hostname);
+
     subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
     if (subreq == NULL) {
         DEBUG(SSSDBG_OP_FAILURE,
@@ -819,6 +1127,7 @@ ad_gpo_process_som_done(struct tevent_req *subreq)
                                      state->ev,
                                      state->sdap_op,
                                      state->opts,
+                                     state->server_hostname,
                                      state->timeout,
                                      som_list);
     if (subreq == NULL) {
@@ -840,6 +1149,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)
@@ -878,7 +1194,7 @@ ad_gpo_process_gpo_done(struct tevent_req *subreq)
                                      &state->num_dacl_filtered_gpos);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
-              "Unable to filter GPO list: [%d](%s)\n",
+              "Unable to filter GPO list by DACKL: [%d](%s)\n",
               ret, sss_strerror(ret));
         goto done;
     }
@@ -896,10 +1212,130 @@ ad_gpo_process_gpo_done(struct tevent_req *subreq)
               state->dacl_filtered_gpos[i]->gpo_guid);
     }
 
-    /* TBD: initiate SMB retrieval */
-    DEBUG(SSSDBG_TRACE_FUNC, "time for SMB retrieval\n");
+    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);
 
-    ret = EOK;
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to filter GPO list by CSE_GUID: [%d](%s)\n",
+               ret, strerror(ret));
+        goto done;
+    }
+
+    if (state->cse_filtered_gpos[0] == NULL) {
+        /* no gpos contain "SecuritySettings" cse_guid, nothing to enforce */
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "no applicable gpos found after cse_guid filtering\n");
+        ret = EOK;
+        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);
+    }
+
+    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];
+
+    /* cse_filtered_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);
+
+    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_size, &allowed_sids,
+                                  &denied_size, &denied_sids);
+
+    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_OP_FAILURE,
+              "GPO access check failed: [%d](%s)\n",
+              ret, strerror(ret));
+        goto done;
+    }
+
+    ret = ad_gpo_cse_step(req);
 
  done:
 
@@ -1225,7 +1661,6 @@ static void ad_gpo_site_dn_retrieval_done(struct tevent_req *subreq);
 static errno_t ad_gpo_get_som_attrs_step(struct tevent_req *req);
 static void ad_gpo_get_som_attrs_done(struct tevent_req *subreq);
 
-
 /*
  * This function uses the input target_dn and input domain_name to populate
  * a list of gp_som objects. Each object in this list represents a SOM
@@ -1797,12 +2232,20 @@ ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx,
 
 /*
  * This function converts the input_path to an smb uri, which is used to
- * populate the _converted_path output parameter. The conversion consists of:
- * - prepending "smb:"
- * - replacing each forward slash ('\') with a back slash character ('/')
+ * populate the _converted_path output parameter. The output is constructed by
+ * concatenating the following elements:
+ * - SMB_STANDARD_URI ("smb://")
+ * - server_hostname (which replaces domain_name in input path)
+ * - smb_path (which starts with the slash immediately after the domain name
+ * Additionally, each forward slash ('\') is replaced with a back slash ('/')
+ *
+ * Example: if input_path = "\\foo.com\SysVol\foo.com\..." and
+ * server_hostname = "adserver.foo.com", then _converted_path would be
+ * "smb://adserver.foo.com/SysVol/foo.com/..."
  */
 static errno_t
 ad_gpo_convert_to_smb_uri(TALLOC_CTX *mem_ctx,
+                          char *server_hostname,
                           char *input_path,
                           const char **_converted_path)
 {
@@ -1810,6 +2253,7 @@ ad_gpo_convert_to_smb_uri(TALLOC_CTX *mem_ctx,
     const char delim = '\\';
     int ret;
     int num_seps = 0;
+    char *smb_path;
 
     DEBUG(SSSDBG_TRACE_ALL, "input_path: %s\n", input_path);
 
@@ -1822,9 +2266,13 @@ ad_gpo_convert_to_smb_uri(TALLOC_CTX *mem_ctx,
 
     ptr = input_path;
     while ((ptr = strchr(ptr, delim))) {
-        *ptr = '/';
-        ptr++;
         num_seps++;
+        if (num_seps == 3) {
+            /* keep track of path from third slash onwards (after domain name) */
+            smb_path = ptr;
+        }
+        *ptr = '/';
+        ptr++;
     }
 
     if (num_seps == 0) {
@@ -1832,7 +2280,10 @@ ad_gpo_convert_to_smb_uri(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    *_converted_path = talloc_asprintf(mem_ctx, "smb:%s", input_path);
+    *_converted_path = talloc_asprintf(mem_ctx, "%s%s%s",
+                                       SMB_STANDARD_URI,
+                                       server_hostname,
+                                       smb_path);
     ret = EOK;
 
  done:
@@ -1978,6 +2429,7 @@ struct ad_gpo_process_gpo_state {
     struct tevent_context *ev;
     struct sdap_id_op *sdap_op;
     struct sdap_options *opts;
+    char *server_hostname;
     int timeout;
     struct gp_gpo **candidate_gpos;
     int num_candidate_gpos;
@@ -2001,6 +2453,7 @@ ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
                         struct tevent_context *ev,
                         struct sdap_id_op *sdap_op,
                         struct sdap_options *opts,
+                        char *server_hostname,
                         int timeout,
                         struct gp_som **som_list)
 {
@@ -2017,6 +2470,7 @@ ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
     state->ev = ev;
     state->sdap_op = sdap_op;
     state->opts = opts;
+    state->server_hostname = server_hostname;
     state->timeout = timeout;
     state->gpo_index = -1;
     state->candidate_gpos = NULL;
@@ -2186,7 +2640,8 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
     }
 
     file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
-    ad_gpo_convert_to_smb_uri(state, file_sys_path, &smb_uri);
+    ad_gpo_convert_to_smb_uri(state, state->server_hostname, file_sys_path,
+                              &smb_uri);
 
     gp_gpo->gpo_file_sys_path = talloc_asprintf(gp_gpo, "%s/Machine",
                                                 smb_uri);
@@ -2307,3 +2762,398 @@ 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
+create_cse_send_buffer(TALLOC_CTX *mem_ctx,
+                       char *smb_uri,
+                       struct io_buffer **io_buf)
+{
+    struct io_buffer *buf;
+    size_t rp;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "smb_uri: %s\n", smb_uri);
+    DEBUG(SSSDBG_TRACE_FUNC, "strlen(smb_uri): %zu\n", strlen(smb_uri));
+
+    buf = talloc(mem_ctx, struct io_buffer);
+    if (buf == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
+        return ENOMEM;
+    }
+
+    buf->size = 1 * sizeof(uint32_t);
+    if (smb_uri) {
+        buf->size += strlen(smb_uri);
+    }
+
+    DEBUG(SSSDBG_TRACE_ALL, "buffer size: %zu\n", buf->size);
+
+    buf->data = talloc_size(buf, buf->size);
+    if (buf->data == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
+        talloc_free(buf);
+        return ENOMEM;
+    }
+
+    rp = 0;
+    /* smb_uri */
+    if (smb_uri) {
+        SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(smb_uri), &rp);
+        safealign_memcpy(&buf->data[rp], smb_uri, strlen(smb_uri), &rp);
+    } else {
+        SAFEALIGN_SET_UINT32(&buf->data[rp], 0, &rp);
+    }
+
+    *io_buf = buf;
+    return EOK;
+}
+
+static errno_t
+parse_gpo_child_response(TALLOC_CTX *mem_ctx,
+                         uint8_t *buf, ssize_t size,
+                         char ***_allowed_sids,
+                         int *_allowed_size,
+                         char ***_denied_sids,
+                         int *_denied_size)
+{
+    size_t p = 0;
+    uint32_t res;
+    int allowed_size = 0;
+    int denied_size = 0;
+    int i = 0;
+    int sid_len = 0;
+    char **allowed_sids;
+    char *allowed_sid;
+    char **denied_sids;
+    char *denied_sid;
+
+    /* operation result code */
+    SAFEALIGN_COPY_UINT32_CHECK(&res, buf + p, size, &p);
+
+    /* allowed_size */
+    SAFEALIGN_COPY_UINT32_CHECK(&allowed_size, buf + p, size, &p);
+    DEBUG(SSSDBG_TRACE_FUNC, "child response allowed_size: %d\n", allowed_size);
+
+    allowed_sids = talloc_array(mem_ctx, char *, allowed_size + 1);
+    if (allowed_sids == NULL) {
+        return ENOMEM;
+    }
+
+    for (i = 0; i < allowed_size; i++) {
+        SAFEALIGN_COPY_UINT32_CHECK(&sid_len, buf + p, size, &p);
+        if ((p + sid_len ) > size) return EINVAL;
+        allowed_sid = talloc_size(mem_ctx, sizeof(char) * (sid_len + 1));
+        if (allowed_sid == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
+            return ENOMEM;
+        }
+        safealign_memcpy(allowed_sid, buf+p, sizeof(char) * sid_len, &p);
+        allowed_sid[sid_len] = '\0';
+
+        allowed_sids[i] = talloc_strdup(allowed_sids, allowed_sid);
+        if (allowed_sids[i] == NULL) {
+            return ENOMEM;
+        }
+    }
+
+    /* denied_size */
+    SAFEALIGN_COPY_UINT32_CHECK(&denied_size, buf + p, size, &p);
+    DEBUG(SSSDBG_TRACE_FUNC, "child response denied_size: %d\n", denied_size);
+
+    denied_sids = talloc_array(mem_ctx, char *, denied_size + 1);
+    if (denied_sids == NULL) {
+        return ENOMEM;
+    }
+
+    for (i = 0; i < denied_size; i++) {
+        SAFEALIGN_COPY_UINT32_CHECK(&sid_len, buf + p, size, &p);
+        if ((p + sid_len ) > size) return EINVAL;
+        denied_sid = talloc_size(mem_ctx, sizeof(char) * (sid_len + 1));
+        if (denied_sid == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
+            return ENOMEM;
+        }
+        safealign_memcpy(denied_sid, buf+p, sizeof(char) * sid_len, &p);
+        denied_sid[sid_len] = '\0';
+
+        denied_sids[i] = talloc_strdup(denied_sids, denied_sid);
+        if (denied_sids[i] == NULL) {
+            return ENOMEM;
+        }
+    }
+
+    *_allowed_size = allowed_size;
+    *_allowed_sids = talloc_steal(mem_ctx, allowed_sids);
+    *_denied_size = denied_size;
+    *_denied_sids = talloc_steal(mem_ctx, denied_sids);
+
+    return EOK;
+}
+
+/* == ad_gpo_process_cse_send/recv implementation ========================== */
+
+struct ad_gpo_process_cse_state {
+    struct tevent_context *ev;
+    pid_t child_pid;
+    uint8_t *buf;
+    ssize_t len;
+    struct io *io;
+};
+
+struct io {
+    int read_from_child_fd;
+    int write_to_child_fd;
+};
+
+static errno_t
+gpo_child_io_destructor(void *ptr)
+{
+    int ret;
+    struct io *io;
+
+    io = talloc_get_type(ptr, struct io);
+    if (io == NULL) return EOK;
+
+    if (io->write_to_child_fd != -1) {
+        ret = close(io->write_to_child_fd);
+        io->write_to_child_fd = -1;
+        if (ret != EOK) {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "close failed [%d][%s].\n", ret, strerror(ret));
+        }
+    }
+
+    if (io->read_from_child_fd != -1) {
+        ret = close(io->read_from_child_fd);
+        io->read_from_child_fd = -1;
+        if (ret != EOK) {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "close failed [%d][%s].\n", ret, strerror(ret));
+        }
+    }
+
+    return EOK;
+}
+
+static errno_t gpo_fork_child(struct tevent_req *req);
+static void gpo_cse_step(struct tevent_req *subreq);
+static void gpo_cse_done(struct tevent_req *subreq);
+
+/*
+ * 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 tevent_req *req;
+    struct tevent_req *subreq;
+    struct ad_gpo_process_cse_state *state;
+    struct io_buffer *buf = NULL;
+    errno_t ret;
+
+    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->buf = NULL;
+    state->len = 0;
+
+    state->io = talloc(state, struct io);
+    if (state->io == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    state->io->write_to_child_fd = -1;
+    state->io->read_from_child_fd = -1;
+    talloc_set_destructor((void *) state->io, gpo_child_io_destructor);
+
+    /* prepare the data to pass to child */
+    ret = create_cse_send_buffer(state, smb_uri, &buf);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "create_cse_send_buffer failed.\n");
+        goto fail;
+    }
+
+    ret = gpo_fork_child(req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "gpo_fork_child failed.\n");
+        goto fail;
+    }
+
+    subreq = write_pipe_send(state, ev, buf->data, buf->size,
+                             state->io->write_to_child_fd);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+    tevent_req_set_callback(subreq, gpo_cse_step, req);
+
+    return req;
+
+fail:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void gpo_cse_step(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct ad_gpo_process_cse_state *state;
+    int ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ad_gpo_process_cse_state);
+
+    ret = write_pipe_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    close(state->io->write_to_child_fd);
+    state->io->write_to_child_fd = -1;
+
+    subreq = read_pipe_send(state, state->ev, state->io->read_from_child_fd);
+
+    if (subreq == NULL) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+    tevent_req_set_callback(subreq, gpo_cse_done, req);
+}
+
+static void gpo_cse_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct ad_gpo_process_cse_state *state;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ad_gpo_process_cse_state);
+    int ret;
+
+    ret = read_pipe_recv(subreq, state, &state->buf, &state->len);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    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 *_allowed_size,
+                            char ***_allowed_sids,
+                            int *_denied_size,
+                            char ***_denied_sids)
+{
+    int ret;
+    char **allowed_sids;
+    int allowed_size;
+    char **denied_sids;
+    int denied_size;
+    struct ad_gpo_process_cse_state *state;
+
+    state = tevent_req_data(req, struct ad_gpo_process_cse_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    ret = parse_gpo_child_response(mem_ctx, state->buf, state->len,
+                                   &allowed_sids,
+                                   &allowed_size,
+                                   &denied_sids,
+                                   &denied_size);
+
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Cannot parse child response: [%d][%s]\n", ret, strerror(ret));
+        return ret;
+    }
+
+    *_allowed_size = allowed_size;
+    *_allowed_sids = talloc_steal(mem_ctx, allowed_sids);
+    *_denied_size = denied_size;
+    *_denied_sids = talloc_steal(mem_ctx, denied_sids);
+
+    return EOK;
+}
+
+static errno_t
+gpo_fork_child(struct tevent_req *req)
+{
+    int pipefd_to_child[2];
+    int pipefd_from_child[2];
+    pid_t pid;
+    int ret;
+    errno_t err;
+    struct ad_gpo_process_cse_state *state;
+
+    state = tevent_req_data(req, struct ad_gpo_process_cse_state);
+
+    ret = pipe(pipefd_from_child);
+    if (ret == -1) {
+        err = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "pipe failed [%d][%s].\n", errno, strerror(errno));
+        return err;
+    }
+    ret = pipe(pipefd_to_child);
+    if (ret == -1) {
+        err = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "pipe failed [%d][%s].\n", errno, strerror(errno));
+        return err;
+    }
+
+    pid = fork();
+
+    if (pid == 0) { /* child */
+        err = exec_child(state,
+                         pipefd_to_child, pipefd_from_child,
+                         GPO_CHILD, gpo_child_debug_fd);
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec gpo_child: [%d][%s].\n",
+              err, strerror(err));
+        return err;
+    } else if (pid > 0) { /* parent */
+        state->child_pid = pid;
+        state->io->read_from_child_fd = pipefd_from_child[0];
+        close(pipefd_from_child[1]);
+        state->io->write_to_child_fd = pipefd_to_child[1];
+        close(pipefd_to_child[0]);
+        fd_nonblocking(state->io->read_from_child_fd);
+        fd_nonblocking(state->io->write_to_child_fd);
+
+        ret = child_handler_setup(state->ev, pid, NULL, NULL, NULL);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Could not set up child signal handler\n");
+            return ret;
+        }
+    } else { /* error */
+        err = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "fork failed [%d][%s].\n", errno, strerror(errno));
+        return err;
+    }
+
+    return EOK;
+}
diff --git a/src/providers/ad/ad_gpo_child.c b/src/providers/ad/ad_gpo_child.c
new file mode 100644
index 0000000000000000000000000000000000000000..899dbb242becda27efb6dfd913c4a2677b978891
--- /dev/null
+++ b/src/providers/ad/ad_gpo_child.c
@@ -0,0 +1,630 @@
+/*
+    SSSD
+
+    AD GPO Backend Module -- perform SMB and CSE processing in a child process
+
+    Authors:
+        Yassir Elley <yelley@redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <popt.h>
+#include <libsmbclient.h>
+#include <ini_configobj.h>
+#include <security/pam_modules.h>
+
+#include "util/util.h"
+#include "util/child_common.h"
+#include "providers/dp_backend.h"
+#include "sss_cli.h"
+
+#define RIGHTS_SECTION "Privilege Rights"
+#define ALLOW_LOGON_LOCALLY "SeInteractiveLogonRight"
+#define DENY_LOGON_LOCALLY "SeDenyInteractiveLogonRight"
+#define SMB_BUFFER_SIZE 65536
+
+struct input_buffer {
+    const char *smb_uri;
+};
+
+static errno_t
+unpack_buffer(uint8_t *buf,
+              size_t size,
+              struct input_buffer *ibuf)
+{
+    size_t p = 0;
+    uint32_t len;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "total buffer size: %zu\n", size);
+
+    /* smb_uri size and length */
+    SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
+
+    DEBUG(SSSDBG_TRACE_FUNC, "smb_uri size: %d\n", len);
+    if (len) {
+        if ((p + len ) > size) return EINVAL;
+        ibuf->smb_uri = talloc_strndup(ibuf, (char *)(buf + p), len);
+        if (ibuf->smb_uri == NULL) return ENOMEM;
+        DEBUG(SSSDBG_TRACE_FUNC, "got smb_uri: %s\n", ibuf->smb_uri);
+        p += len;
+    }
+
+    return EOK;
+}
+
+
+static errno_t
+pack_buffer(struct response *r,
+            int result,
+            int allowed_size,
+            char **allowed_sids,
+            int denied_size,
+            char **denied_sids)
+{
+    int len = 0;
+    size_t p = 0;
+    int i;
+    int sid_len = 0;
+
+    /* A buffer with the following structure must be created:
+     * int32_t status of the request (required)
+     * int32_t allowed_size  (required)
+     * uint8_t[allowed_size] (optional if allowed_size == 0)
+     * int32_t denied_size   (required)
+     * uint8_t[denied_size]  (optional if denied_size == 0)
+     */
+
+    DEBUG(SSSDBG_TRACE_FUNC, "entering pack_buffer\n");
+
+    for (i = 0; i < allowed_size; i++) {
+        len += strlen(allowed_sids[i]);
+    }
+
+    for (i = 0; i < denied_size; i++) {
+        len += strlen(denied_sids[i]);
+    }
+
+    r->size = (3 + allowed_size + denied_size) * sizeof(uint32_t) + len;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "response size: %zu\n",r->size);
+
+    r->buf = talloc_array(r, uint8_t, r->size);
+    if(r->buf == NULL) {
+        return ENOMEM;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "result [%d] allowed_size [%d] denied_size [%d]\n",
+          result, allowed_size, denied_size);
+
+    /* result */
+    SAFEALIGN_SET_UINT32(&r->buf[p], result, &p);
+
+    /* allowed_size */
+    SAFEALIGN_SET_UINT32(&r->buf[p], allowed_size, &p);
+
+    /* allowed_sids */
+    for (i = 0; i < allowed_size; i++) {
+        sid_len = strlen(allowed_sids[i]);
+        SAFEALIGN_SET_UINT32(&r->buf[p], sid_len, &p);
+        safealign_memcpy(&r->buf[p], allowed_sids[i], sid_len, &p);
+    }
+
+    /* denied_size */
+    SAFEALIGN_SET_UINT32(&r->buf[p], denied_size, &p);
+
+    /* denied_sids */
+    for (i = 0; i < denied_size; i++) {
+        sid_len = strlen(denied_sids[i]);
+        SAFEALIGN_SET_UINT32(&r->buf[p], sid_len, &p);
+        safealign_memcpy(&r->buf[p], denied_sids[i], sid_len, &p);
+    }
+
+    return EOK;
+}
+
+static errno_t
+prepare_response(TALLOC_CTX *mem_ctx,
+                 int result,
+                 int allowed_size,
+                 char **allowed_sids,
+                 int denied_size,
+                 char **denied_sids,
+                 struct response **rsp)
+{
+    int ret;
+    struct response *r = NULL;
+
+    DEBUG(SSSDBG_TRACE_FUNC, "entering prepare_response.\n");
+    r = talloc_zero(mem_ctx, struct response);
+    if (r == NULL) {
+        return ENOMEM;
+    }
+
+    r->buf = NULL;
+    r->size = 0;
+
+    ret = pack_buffer(r, result, allowed_size, allowed_sids, denied_size, denied_sids);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "pack_buffer failed\n");
+        return ret;
+    }
+
+    *rsp = r;
+    DEBUG(SSSDBG_TRACE_FUNC, "r->size: %zu\n", r->size);
+    return EOK;
+}
+
+/*
+ * This function uses the input ini_config object to parse the logon right value
+ * associated with the input name. This value is a list of sids, and is used
+ * to populate the output parameters. The input name can be either
+ * ALLOW_LOGON_LOCALLY or DENY_LOGON_LOCALLY.
+ */
+static errno_t
+parse_logon_right_with_libini(TALLOC_CTX *mem_ctx,
+                              struct ini_cfgobj *ini_config,
+                              const char *name,
+                              int *_size,
+                              char ***_sids)
+{
+    int ret = 0;
+    struct value_obj *vobj = NULL;
+    char **ini_sids = NULL;
+    char *ini_sid = NULL;
+    int num_ini_sids = 0;
+    char **sids = NULL;
+    int i;
+    TALLOC_CTX *tmp_ctx = NULL;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = ini_get_config_valueobj(RIGHTS_SECTION, name, ini_config,
+                                  INI_GET_FIRST_VALUE, &vobj);
+    if (ret != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ini_get_config_valueobj failed [%d][%s]\n", ret, strerror(ret));
+        goto done;
+    }
+    if (vobj == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "section/name not found: [%s][%s]\n",
+              RIGHTS_SECTION, name);
+        ret = EOK;
+        goto done;
+    }
+    ini_sids = ini_get_string_config_array(vobj, NULL, &num_ini_sids, &ret);
+
+    if (ret != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ini_get_string_config_array failed [%d][%s]\n", ret, strerror(ret));
+        goto done;
+    }
+
+    sids = talloc_array(tmp_ctx, char *, num_ini_sids + 1);
+    if (sids == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (i = 0; i < num_ini_sids; i++) {
+        ini_sid = ini_sids[i];
+
+        /* remove the asterisk prefix found on sids in the .inf policy file */
+        if (ini_sid[0] == '*') {
+            ini_sid++;
+        }
+        sids[i] = talloc_strdup(sids, ini_sid);
+        if (sids[i] == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+    sids[i] = NULL;
+
+    *_size = num_ini_sids;
+    *_sids = talloc_steal(mem_ctx, sids);
+
+    ret = EOK;
+
+ done:
+
+    ini_free_string_config_array(ini_sids);
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+/*
+ * This function parses the cse-specific (GP_EXT_GUID_SECURITY) input data_buf,
+ * and uses the results to populate the output parameters with the list of
+ * allowed_sids and denied_sids
+ */
+static errno_t
+ad_gpo_parse_security_cse_buffer(TALLOC_CTX *mem_ctx,
+                                 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;
+    const char *key = NULL;
+    TALLOC_CTX *tmp_ctx = NULL;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = ini_config_create(&ini_config);
+    if (ret != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ini_config_create failed [%d][%s]\n", ret, strerror(ret));
+        goto done;
+    }
+
+    ret = ini_config_file_from_mem(data_buf, data_len, &file_ctx);
+    if (ret != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ini_config_file_from_mem failed [%d][%s]\n", ret, strerror(ret));
+        goto done;
+    }
+
+    ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, 0, 0, ini_config);
+    if (ret != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ini_config_parse failed [%d][%s]\n", ret, strerror(ret));
+        goto done;
+    }
+
+    key = ALLOW_LOGON_LOCALLY;
+    ret = parse_logon_right_with_libini(tmp_ctx,
+                                        ini_config,
+                                        key,
+                                        &allow_size,
+                                        &allow_sids);
+    if (ret != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "parse_logon_right_with_libini failed for %s [%d][%s]\n",
+              key, ret, strerror(ret));
+        goto done;
+    }
+
+    key = DENY_LOGON_LOCALLY;
+    ret = parse_logon_right_with_libini(tmp_ctx,
+                                        ini_config,
+                                        DENY_LOGON_LOCALLY,
+                                        &deny_size,
+                                        &deny_sids);
+    if (ret != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "parse_logon_right_with_libini failed for %s [%d][%s]\n",
+              key, ret, strerror(ret));
+        goto done;
+    }
+
+    *allowed_sids = talloc_steal(mem_ctx, allow_sids);
+    *allowed_size = allow_size;
+    *denied_sids = talloc_steal(mem_ctx, deny_sids);
+    *denied_size = deny_size;
+
+ done:
+
+    if (ret != EOK) {
+      DEBUG(SSSDBG_CRIT_FAILURE, "Error encountered: %d.\n", ret);
+    }
+
+    ini_config_file_destroy(file_ctx);
+    ini_config_destroy(ini_config);
+    talloc_free(tmp_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;
+    TALLOC_CTX *tmp_ctx = NULL;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    DEBUG(SSSDBG_TRACE_ALL, "%s\n", smb_uri);
+
+    context = smbc_new_context();
+    if (context == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate new smbc context\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    smbc_setFunctionAuthData(context, sssd_krb_get_auth_data_fn);
+    smbc_setOptionUseKerberos(context, 1);
+
+    /* Initialize the context using the previously specified options */
+    if (smbc_init_context(context) == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize smbc context\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* 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");
+        ret = EPIPE;
+        goto done;
+    }
+
+    buf = talloc_array(tmp_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");
+        ret = EPIPE;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_CRIT_FAILURE, "bytesread: %d\n", bytesread);
+
+    smbc_close(remotehandle);
+
+    ret = ad_gpo_parse_security_cse_buffer(tmp_ctx,
+                                           buf,
+                                           bytesread,
+                                           &allowed_sids,
+                                           &allowed_size,
+                                           &denied_sids,
+                                           &denied_size);
+
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "ad_gpo_parse_security_cse_buffer failed [%d][%s]\n",
+              ret, strerror(ret));
+        goto done;
+    }
+
+    /* TBD: allowed/denied_sids/size should be stored in cache */
+
+    *_allowed_sids = talloc_steal(mem_ctx, allowed_sids);
+    *_allowed_size = allowed_size;
+    *_denied_sids = talloc_steal(mem_ctx, denied_sids);
+    *_denied_size = denied_size;
+
+ done:
+    smbc_free_context(context, 0);
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+int
+main(int argc, const char *argv[])
+{
+    int opt;
+    poptContext pc;
+    int debug_fd = -1;
+    errno_t ret;
+    int result;
+    TALLOC_CTX *main_ctx = NULL;
+    uint8_t *buf = NULL;
+    ssize_t len = 0;
+    struct input_buffer *ibuf = NULL;
+    struct response *resp = NULL;
+    size_t written;
+    char **allowed_sids;
+    int allowed_size;
+    char **denied_sids;
+    int denied_size;
+    int j;
+
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0,
+         _("Debug level"), NULL},
+        {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0,
+         _("Add debug timestamps"), NULL},
+        {"debug-microseconds", 0, POPT_ARG_INT, &debug_microseconds, 0,
+         _("Show timestamps with microseconds"), NULL},
+        {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
+         _("An open file descriptor for the debug logs"), NULL},
+        POPT_TABLEEND
+    };
+
+    /* Set debug level to invalid value so we can decide if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+        fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                  poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            _exit(-1);
+        }
+    }
+
+    poptFreeContext(pc);
+
+    DEBUG_INIT(debug_level);
+
+    debug_prg_name = talloc_asprintf(NULL, "[sssd[gpo_child[%d]]]", getpid());
+    if (debug_prg_name == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
+        goto fail;
+    }
+
+    if (debug_fd != -1) {
+        ret = set_debug_file_from_fd(debug_fd);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
+        }
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "gpo_child started.\n");
+
+    main_ctx = talloc_new(NULL);
+    if (main_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
+        talloc_free(discard_const(debug_prg_name));
+        goto fail;
+    }
+    talloc_steal(main_ctx, debug_prg_name);
+
+    buf = talloc_size(main_ctx, sizeof(uint8_t)*IN_BUF_SIZE);
+    if (buf == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
+        goto fail;
+    }
+
+    ibuf = talloc_zero(main_ctx, struct input_buffer);
+    if (ibuf == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
+        goto fail;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "context initialized\n");
+
+    errno = 0;
+    len = sss_atomic_read_s(STDIN_FILENO, buf, IN_BUF_SIZE);
+    if (len == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, "read failed [%d][%s].\n", ret, strerror(ret));
+        goto fail;
+    }
+
+    close(STDIN_FILENO);
+
+    ret = unpack_buffer(buf, len, ibuf);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "unpack_buffer failed.[%d][%s].\n", ret, strerror(ret));
+        goto fail;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "processing security settings\n");
+
+    result = process_security_settings_cse(main_ctx,
+                                           ibuf->smb_uri,
+                                           &allowed_sids,
+                                           &allowed_size,
+                                           &denied_sids,
+                                           &denied_size);
+    if (result != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "process_security_settings_cse failed.[%d][%s].\n",
+              ret, strerror(ret));
+        goto fail;
+    }
+
+    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 = prepare_response(main_ctx, result, allowed_size, allowed_sids, denied_size, denied_sids, &resp);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "prepare_response failed. [%d][%s].\n",
+                    ret, strerror(ret));
+        goto fail;
+    }
+
+    errno = 0;
+    DEBUG(SSSDBG_TRACE_FUNC, "resp->size: %zu\n", resp->size);
+
+    written = sss_atomic_write_s(STDOUT_FILENO, resp->buf, resp->size);
+    if (written == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, "write failed [%d][%s].\n", ret,
+                    strerror(ret));
+        goto fail;
+    }
+
+    if (written != resp->size) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Expected to write %zu bytes, wrote %zu\n",
+              resp->size, written);
+        goto fail;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "gpo_child completed successfully\n");
+    close(STDOUT_FILENO);
+    talloc_free(main_ctx);
+    return EXIT_SUCCESS;
+
+fail:
+    DEBUG(SSSDBG_CRIT_FAILURE, "gpo_child failed!\n");
+    close(STDOUT_FILENO);
+    talloc_free(main_ctx);
+    return EXIT_FAILURE;
+}
-- 
1.8.5.3

