From 19d3aba12c70528708be9440aca66038a291f29e Mon Sep 17 00:00:00 2001 From: Yassir Elley Date: Wed, 21 May 2014 15:11:36 -0400 Subject: AD-GPO: Add gpo-smb implementation in gpo_child process Reviewed-by: Sumit Bose --- Makefile.am | 22 + contrib/sssd.spec.in | 2 +- src/external/samba.m4 | 9 + src/providers/ad/ad_gpo.c | 888 +++++++++++++++++++++++++++++++++++++++- src/providers/ad/ad_gpo_child.c | 637 ++++++++++++++++++++++++++++ 5 files changed, 1546 insertions(+), 12 deletions(-) create mode 100644 src/providers/ad/ad_gpo_child.c diff --git a/Makefile.am b/Makefile.am index 83999f358..6cf5d8cb0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -121,6 +121,9 @@ endif if BUILD_IFP sssdlibexec_PROGRAMS += sssd_ifp endif +if BUILD_SAMBA +sssdlibexec_PROGRAMS += gpo_child +endif if BUILD_PAC_RESPONDER @@ -2298,6 +2301,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 b5e790371..10704ae48 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 735cc5a18..5cd5f0986 100644 --- a/src/external/samba.m4 +++ b/src/external/samba.m4 @@ -17,6 +17,15 @@ with argument --without-samba libsmbclient 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 + ]])) + + PKG_CHECK_MODULES(INI_CONFIG, ini_config >= 1.1.0, , + AC_MSG_ERROR([[Please install libini_config development libraries. +libini_config libraries are necessary for building ipa provider, as well +as for building gpo-based access control in ad 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 bb6896e70..7928e6faa 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 #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 #include +/* == 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,24 @@ 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/..." + * + * Note that the input_path must have at least three forward slash separators. + * For example, input_path = "\\foo.com" is not a valid input_path, because + * it has only two forward slash separators. */ 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 +2257,7 @@ ad_gpo_convert_to_smb_uri(TALLOC_CTX *mem_ctx, const char delim = '\\'; int ret; int num_seps = 0; + char *smb_path = NULL; DEBUG(SSSDBG_TRACE_ALL, "input_path: %s\n", input_path); @@ -1822,9 +2270,13 @@ ad_gpo_convert_to_smb_uri(TALLOC_CTX *mem_ctx, ptr = input_path; while ((ptr = strchr(ptr, delim))) { + num_seps++; + if (num_seps == 3) { + /* keep track of path from third slash onwards (after domain name) */ + smb_path = ptr; + } *ptr = '/'; ptr++; - num_seps++; } if (num_seps == 0) { @@ -1832,7 +2284,15 @@ ad_gpo_convert_to_smb_uri(TALLOC_CTX *mem_ctx, goto done; } - *_converted_path = talloc_asprintf(mem_ctx, "smb:%s", input_path); + if (smb_path == NULL) { + ret = EINVAL; + goto done; + } + + *_converted_path = talloc_asprintf(mem_ctx, "%s%s%s", + SMB_STANDARD_URI, + server_hostname, + smb_path); ret = EOK; done: @@ -1978,6 +2438,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 +2462,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 +2479,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 +2649,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 +2771,405 @@ 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; + int smb_uri_length; + + smb_uri_length = strlen(smb_uri); + + DEBUG(SSSDBG_TRACE_FUNC, "smb_uri: %s\n", smb_uri); + DEBUG(SSSDBG_TRACE_FUNC, "strlen(smb_uri): %d\n", smb_uri_length); + + 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); + buf->size += smb_uri_length; + + 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 */ + SAFEALIGN_SET_UINT32(&buf->data[rp], smb_uri_length, &rp); + safealign_memcpy(&buf->data[rp], smb_uri, smb_uri_length, &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; + errno_t ret; + int allowed_size = 0; + int denied_size = 0; + int i = 0; + int sid_len = 0; + char **allowed_sids; + char **denied_sids; + TALLOC_CTX *tmp_ctx = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + /* 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(tmp_ctx, char *, allowed_size); + if (allowed_sids == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < allowed_size; i++) { + SAFEALIGN_COPY_UINT32_CHECK(&sid_len, buf + p, size, &p); + if ((p + sid_len ) > size) { + ret = EINVAL; + goto done; + } + allowed_sids[i] = talloc_strndup(allowed_sids, + (const char *)buf + p, + sid_len); + if (allowed_sids[i] == NULL) { + ret = ENOMEM; + goto done; + } + p += sid_len; + } + + /* 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(tmp_ctx, char *, denied_size); + if (denied_sids == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < denied_size; i++) { + SAFEALIGN_COPY_UINT32_CHECK(&sid_len, buf + p, size, &p); + if ((p + sid_len ) > size) { + ret = EINVAL; + goto done; + } + denied_sids[i] = talloc_strndup(denied_sids, + (const char *)buf + p, + sid_len); + if (denied_sids[i] == NULL) { + ret = ENOMEM; + goto done; + } + p += sid_len; + } + + *_allowed_size = allowed_size; + *_allowed_sids = talloc_steal(mem_ctx, allowed_sids); + *_denied_size = denied_size; + *_denied_sids = talloc_steal(mem_ctx, denied_sids); + + ret = EOK; + + done: + talloc_free(tmp_ctx); + return ret; +} + +/* == 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 000000000..0b6a4cde3 --- /dev/null +++ b/src/providers/ad/ad_gpo_child.c @@ -0,0 +1,637 @@ +/* + SSSD + + AD GPO Backend Module -- perform SMB and CSE processing in a child process + + Authors: + Yassir Elley + + 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include + +#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 == 0) { + return EINVAL; + } else { + 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: + * uint32_t status of the request (required) + * uint32_t allowed_size (required) + * sid_message* (allowed_size instances) + * uint32_t denied_size (required) + * sid_message* (denied_size instances) + * + * A sid_message consists of: + * uint32_t sid_len + * uint8_t[sid_len] sid string + */ + + 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; +} -- cgit