diff options
-rw-r--r-- | Makefile.am | 22 | ||||
-rw-r--r-- | contrib/sssd.spec.in | 2 | ||||
-rw-r--r-- | src/external/samba.m4 | 9 | ||||
-rw-r--r-- | src/providers/ad/ad_gpo.c | 888 | ||||
-rw-r--r-- | src/providers/ad/ad_gpo_child.c | 637 |
5 files changed, 1546 insertions, 12 deletions
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 @@ -19,4 +19,13 @@ 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 <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,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 <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 == 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; +} |