From b42bb7d9dbf9a4c44a03e7bf1bab471a8a85e858 Mon Sep 17 00:00:00 2001 From: Michal Zidek Date: Mar 19 2013 16:50:53 +0000 Subject: Move SELinux processing to provider. The SELinux processing was distributed between provider and pam responder which resulted in hard to maintain code. This patch moves the logic to provider. IT ALSO REQUIRES CHANGE IN THE SELINUX POLICY, because the provider also writes the content of selinux login file to disk (which was done by responder before). https://fedorahosted.org/sssd/ticket/1743 --- diff --git a/src/db/sysdb_selinux.c b/src/db/sysdb_selinux.c index b27e0a9..80cfe53 100644 --- a/src/db/sysdb_selinux.c +++ b/src/db/sysdb_selinux.c @@ -336,113 +336,6 @@ sysdb_get_selinux_usermaps(TALLOC_CTX *mem_ctx, return EOK; } -errno_t sysdb_search_selinux_usermap_by_username(TALLOC_CTX *mem_ctx, - struct sysdb_ctx *sysdb, - struct sss_domain_info *domain, - const char *username, - struct ldb_message ***_usermaps) -{ - TALLOC_CTX *tmp_ctx; - struct ldb_message **msgs = NULL; - const char *attrs[] = { SYSDB_NAME, - SYSDB_USER_CATEGORY, - SYSDB_HOST_CATEGORY, - SYSDB_ORIG_MEMBER_USER, - SYSDB_ORIG_MEMBER_HOST, - SYSDB_SELINUX_HOST_PRIORITY, - SYSDB_SELINUX_USER, - NULL }; - struct sysdb_attrs *user; - struct sysdb_attrs *tmp_attrs; - struct ldb_message **usermaps = NULL; - size_t msgs_count = 0; - size_t usermaps_cnt; - uint32_t priority = 0; - uint32_t host_priority = 0; - uint32_t top_priority = 0; - errno_t ret; - int i; - - tmp_ctx = talloc_new(NULL); - if (!tmp_ctx) { - return ENOMEM; - } - - /* Now extract user attributes */ - ret = sss_selinux_extract_user(tmp_ctx, sysdb, domain, username, &user); - if (ret != EOK) { - goto done; - } - - /* Now extract all SELinux user maps */ - ret = sysdb_get_selinux_usermaps(tmp_ctx, sysdb, domain, - attrs, &msgs_count, &msgs); - if (ret) { - goto done; - } - - /* Now filter those that match */ - tmp_attrs = talloc_zero(tmp_ctx, struct sysdb_attrs); - if (tmp_attrs == NULL) { - ret = ENOMEM; - goto done; - } - - usermaps = talloc_zero_array(tmp_ctx, struct ldb_message *, msgs_count + 1); - if (usermaps == NULL) { - ret = ENOMEM; - goto done; - } - - usermaps_cnt = 0; - for (i = 0; i < msgs_count; i++) { - tmp_attrs->a = msgs[i]->elements; - tmp_attrs->num = msgs[i]->num_elements; - - if (sss_selinux_match(tmp_attrs, user, NULL, &priority)) { - priority &= ~(SELINUX_PRIORITY_HOST_NAME | - SELINUX_PRIORITY_HOST_GROUP | - SELINUX_PRIORITY_HOST_CAT); - - /* Now figure out host priority */ - ret = sysdb_attrs_get_uint32_t(tmp_attrs, - SYSDB_SELINUX_HOST_PRIORITY, - &host_priority); - if (ret != EOK) { - continue; - } - - priority += host_priority; - if (priority < top_priority) { - /* This rule has lower priority than what we already have, - * skip it */ - continue; - } else if (priority > top_priority) { - /* If the rule has higher priority, drop what we already - * have */ - while (usermaps_cnt > 0) { - usermaps_cnt--; - talloc_zfree(usermaps[usermaps_cnt]); - } - top_priority = priority; - } - - - usermaps[usermaps_cnt] = talloc_steal(usermaps, msgs[i]); - usermaps_cnt++; - } else { - talloc_zfree(msgs[i]); - } - } - - *_usermaps = talloc_steal(mem_ctx, usermaps); - - ret = EOK; -done: - talloc_zfree(tmp_ctx); - return ret; -} - errno_t sysdb_search_selinux_config(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, struct sss_domain_info *domain, diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c index 29e9887..0e65a37 100644 --- a/src/providers/ipa/ipa_selinux.c +++ b/src/providers/ipa/ipa_selinux.c @@ -21,7 +21,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - +#include #include #include "db/sysdb_selinux.h" @@ -37,6 +37,8 @@ #include "providers/ipa/ipa_selinux_common.h" #include "providers/ipa/ipa_selinux_maps.h" +#ifdef HAVE_SELINUX_LOGIN_DIR + static struct tevent_req * ipa_get_selinux_send(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, @@ -65,12 +67,14 @@ static void ipa_get_config_step(struct tevent_req *req); static void ipa_get_selinux_config_done(struct tevent_req *subreq); static void ipa_get_selinux_maps_done(struct tevent_req *subreq); static void ipa_get_selinux_hbac_done(struct tevent_req *subreq); -static errno_t ipa_selinux_process_maps(struct sysdb_attrs *user, +static errno_t ipa_selinux_process_maps(TALLOC_CTX *mem_ctx, + struct sysdb_attrs *user, struct sysdb_attrs *host, struct sysdb_attrs **selinux_maps, size_t selinux_map_count, struct sysdb_attrs **hbac_rules, - size_t hbac_rule_count); + size_t hbac_rule_count, + struct sysdb_attrs ***usermaps); struct ipa_selinux_op_ctx { struct be_req *be_req; @@ -184,6 +188,14 @@ fail: return NULL; } +static errno_t create_order_array(TALLOC_CTX *mem_ctx, const char *map_order, + char ***_order_array, size_t *_order_count); +static errno_t choose_best_seuser(struct sysdb_attrs **usermaps, + struct pam_data *pd, + char **order_array, int order_count, + const char *default_user); + + static void ipa_selinux_handler_done(struct tevent_req *req) { struct ipa_selinux_op_ctx *op_ctx = tevent_req_callback_data(req, struct ipa_selinux_op_ctx); @@ -199,6 +211,9 @@ static void ipa_selinux_handler_done(struct tevent_req *req) char *map_order = NULL; size_t hbac_count = 0; struct sysdb_attrs **hbac_rules = 0; + struct sysdb_attrs **best_match_maps; + size_t order_count; + char **order_array; ret = ipa_get_selinux_recv(req, breq, &map_count, &maps, &hbac_count, &hbac_rules, @@ -207,10 +222,31 @@ static void ipa_selinux_handler_done(struct tevent_req *req) goto fail; } - ret = ipa_selinux_process_maps(op_ctx->user, op_ctx->host, + /* Process the maps and return list of best matches (maps with + * highest priority). The input maps are also parent memory + * context for the output list of best matches. The best match + * maps should never be freed explicitly but always through + * their parent (or any indirect parent) */ + ret = ipa_selinux_process_maps(maps, op_ctx->user, op_ctx->host, maps, map_count, - hbac_rules, hbac_count); + hbac_rules, hbac_count, &best_match_maps); + if (ret != EOK) { + goto fail; + } + + ret = create_order_array(op_ctx, map_order, + &order_array, &order_count); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to create ordered SELinux users array.\n")); + goto fail; + } + + ret = choose_best_seuser(best_match_maps, pd, order_array, order_count, + default_user); if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to evaluate ordered SELinux users array.\n")); goto fail; } @@ -272,22 +308,30 @@ ipa_selinux_process_seealso_maps(struct sysdb_attrs *user, struct sysdb_attrs **seealso_rules, size_t seealso_rules_count, struct sysdb_attrs **hbac_rules, - size_t hbac_rule_count); + size_t hbac_rule_count, + uint32_t top_priority, + struct sysdb_attrs **usermaps, + size_t best_match_maps_cnt); static errno_t -ipa_selinux_process_maps(struct sysdb_attrs *user, +ipa_selinux_process_maps(TALLOC_CTX *mem_ctx, + struct sysdb_attrs *user, struct sysdb_attrs *host, struct sysdb_attrs **selinux_maps, size_t selinux_map_count, struct sysdb_attrs **hbac_rules, - size_t hbac_rule_count) + size_t hbac_rule_count, + struct sysdb_attrs ***_usermaps) { TALLOC_CTX *tmp_ctx; int i; errno_t ret; uint32_t priority = 0; + uint32_t top_priority = 0; struct sysdb_attrs **seealso_rules; - size_t num_seealso_rules; + size_t num_seealso_rules = 0; const char *seealso_str; + struct sysdb_attrs **usermaps; + size_t best_match_maps_cnt = 0; tmp_ctx = talloc_new(NULL); if (!tmp_ctx) { @@ -300,23 +344,36 @@ ipa_selinux_process_maps(struct sysdb_attrs *user, ret = ENOMEM; goto done; } - num_seealso_rules = 0; + + usermaps = talloc_zero_array(tmp_ctx, struct sysdb_attrs *, selinux_map_count + 1); + if (usermaps == NULL) { + ret = ENOMEM; + goto done; + } for (i = 0; i < selinux_map_count; i++) { - if (sss_selinux_match(selinux_maps[i], user, - host, &priority)) { - priority &= ~(SELINUX_PRIORITY_USER_NAME | - SELINUX_PRIORITY_USER_GROUP | - SELINUX_PRIORITY_USER_CAT); - ret = sysdb_attrs_add_uint32(selinux_maps[i], - SYSDB_SELINUX_HOST_PRIORITY, - priority); - if (ret != EOK) { - goto done; + if (sss_selinux_match(selinux_maps[i], user, host, &priority)) { + if (priority < top_priority) { + /* This rule has lower priority than what we already have, + * skip it. */ + continue; + } else if (priority > top_priority) { + /* This rule has higher priority, drop what we already have */ + while (best_match_maps_cnt > 0) { + best_match_maps_cnt--; + usermaps[best_match_maps_cnt] = NULL; + } + top_priority = priority; } + + usermaps[best_match_maps_cnt] = selinux_maps[i]; + best_match_maps_cnt++; + continue; } + /* SELinux map did not matched -> check sealso attribute for + * possible HBAC match */ ret = sysdb_attrs_get_string(selinux_maps[i], SYSDB_SELINUX_SEEALSO, &seealso_str); if (ret == ENOENT) { @@ -331,11 +388,14 @@ ipa_selinux_process_maps(struct sysdb_attrs *user, ret = ipa_selinux_process_seealso_maps(user, host, seealso_rules, num_seealso_rules, - hbac_rules, hbac_rule_count); + hbac_rules, hbac_rule_count, + top_priority, usermaps, best_match_maps_cnt); if (ret != EOK) { goto done; } + *_usermaps = talloc_steal(mem_ctx, usermaps); + ret = EOK; done: talloc_free(tmp_ctx); @@ -348,15 +408,18 @@ ipa_selinux_process_seealso_maps(struct sysdb_attrs *user, struct sysdb_attrs **seealso_rules, size_t seealso_rules_count, struct sysdb_attrs **hbac_rules, - size_t hbac_rule_count) + size_t hbac_rule_count, + uint32_t top_priority, + struct sysdb_attrs **usermaps, + size_t best_match_maps_cnt) { int i, j; errno_t ret; struct ldb_message_element *el; - uint32_t priority = 0; struct sysdb_attrs *usermap; const char *seealso_dn; const char *hbac_dn; + uint32_t priority; for (i = 0; i < hbac_rule_count; i++) { ret = sysdb_attrs_get_string(hbac_rules[i], SYSDB_ORIG_DN, &hbac_dn); @@ -399,16 +462,24 @@ ipa_selinux_process_seealso_maps(struct sysdb_attrs *user, DEBUG(SSSDBG_TRACE_FUNC, ("HBAC rule [%s] matched, copying its" "attributes to SELinux user map [%s]\n", hbac_dn, seealso_dn)); - priority &= ~(SELINUX_PRIORITY_USER_NAME | - SELINUX_PRIORITY_USER_GROUP | - SELINUX_PRIORITY_USER_CAT); - ret = sysdb_attrs_add_uint32(usermap, - SYSDB_SELINUX_HOST_PRIORITY, - priority); - if (ret != EOK) { - return ret; + + /* Selinux maps priority evaluation removed --DELETE this comment before pushing*/ + if (priority < top_priority) { + /* This rule has lower priority than what we already have, + * skip it. */ + continue; + } else if (priority > top_priority) { + /* This rule has higher priority, drop what we already have */ + while (best_match_maps_cnt > 0) { + best_match_maps_cnt--; + usermaps[best_match_maps_cnt] = NULL; + } + top_priority = priority; } + usermaps[best_match_maps_cnt] = usermap; + best_match_maps_cnt++; + ret = sysdb_attrs_copy_values(hbac_rules[i], usermap, SYSDB_ORIG_MEMBER_USER); if (ret != EOK) { return ret; @@ -428,6 +499,271 @@ ipa_selinux_process_seealso_maps(struct sysdb_attrs *user, return EOK; } +static errno_t create_order_array(TALLOC_CTX *mem_ctx, const char *map_order, + char ***_order_array, size_t *_order_count) +{ + TALLOC_CTX *tmp_ctx; + char *order = NULL; + char **order_array; + errno_t ret; + int i; + int len; + size_t order_count; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + order = talloc_strdup(tmp_ctx, map_order); + if (order == NULL) { + ret = ENOMEM; + goto done; + } + len = strlen(order); + + /* The "order" string contains one or more SELinux user records + * separated by $. Now we need to create an array of string from + * this one string. First find out how many elements in the array + * will be. This way only one alloc will be necessary for the array + */ + order_count = 1; + for (i = 0; i < len; i++) { + if (order[i] == '$') order_count++; + } + + order_array = talloc_array(tmp_ctx, char *, order_count); + if (order_array == NULL) { + ret = ENOMEM; + goto done; + } + + /* Now fill the array with pointers to the original string. Also + * use binary zeros to make multiple string out of the one. + */ + order_array[0] = order; + order_count = 1; + for (i = 0; i < len; i++) { + if (order[i] == '$') { + order[i] = '\0'; + order_array[order_count] = &order[i+1]; + order_count++; + } + } + + *_order_array = talloc_steal(mem_ctx, order_array); + *_order_count = order_count; + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t write_selinux_login_file(const char *username, char *string); +static errno_t remove_selinux_login_file(const char *username); + +/* Choose best selinux user based on given order and write + * the user to selinux login file. */ +static errno_t choose_best_seuser(struct sysdb_attrs **usermaps, + struct pam_data *pd, + char **order_array, int order_count, + const char *default_user) +{ + TALLOC_CTX *tmp_ctx; + char *file_content = NULL; + const char *tmp_str; + errno_t ret, err; + int i, j; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n")); + return ENOMEM; + } + + /* If no maps match, we'll use the default SELinux user from the + * config */ + file_content = talloc_strdup(tmp_ctx, default_user); + if (file_content == NULL) { + ret = ENOMEM; + goto done; + } + + /* Iterate through the order array and try to find SELinux users + * in fetched maps. The order array contains all SELinux users + * allowed in the domain in the same order they should appear + * in the SELinux config file. If any user from the order array + * is not in fetched user maps, it means it should not be allowed + * for the user who is just logging in. + * + * Right now we have empty content of the SELinux config file, + * we shall add only those SELinux users that are present both in + * the order array and user maps applicable to the user who is + * logging in. + */ + for (i = 0; i < order_count; i++) { + for (j = 0; usermaps[j] != NULL; j++) { + tmp_str = sss_selinux_map_get_seuser(usermaps[j]); + + if (tmp_str && !strcasecmp(tmp_str, order_array[i])) { + /* If file_content contained something, overwrite it. + * This record has higher priority. + */ + talloc_zfree(file_content); + file_content = talloc_strdup(tmp_ctx, tmp_str); + if (file_content == NULL) { + ret = ENOMEM; + goto done; + } + break; + } + } + } + + ret = write_selinux_login_file(pd->user, file_content); +done: + if (!file_content) { + err = remove_selinux_login_file(pd->user); + /* Don't overwrite original error condition if there was one */ + if (ret == EOK) ret = err; + } + talloc_free(tmp_ctx); + return ret; +} + +static errno_t write_selinux_login_file(const char *username, char *string) +{ + char *path = NULL; + char *tmp_path = NULL; + ssize_t written; + int len; + int fd = -1; + mode_t oldmask; + TALLOC_CTX *tmp_ctx; + char *full_string = NULL; + int enforce; + errno_t ret = EOK; + + len = strlen(string); + if (len == 0) { + return EINVAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n")); + return ENOMEM; + } + + path = selogin_path(tmp_ctx, username); + if (path == NULL) { + ret = ENOMEM; + goto done; + } + + tmp_path = talloc_asprintf(tmp_ctx, "%sXXXXXX", path); + if (tmp_path == NULL) { + ret = ENOMEM; + goto done; + } + + oldmask = umask(022); + fd = mkstemp(tmp_path); + ret = errno; + umask(oldmask); + if (fd < 0) { + if (ret == ENOENT) { + /* if selinux is disabled and selogin dir does not exist, + * just ignore the error */ + if (selinux_getenforcemode(&enforce) == 0 && enforce == -1) { + ret = EOK; + goto done; + } + + /* continue if we can't get enforce mode or selinux is enabled */ + } + + DEBUG(SSSDBG_OP_FAILURE, ("unable to create temp file [%s] " + "for SELinux data [%d]: %s\n", tmp_path, ret, strerror(ret))); + goto done; + } + + full_string = talloc_asprintf(tmp_ctx, "%s:%s", ALL_SERVICES, string); + if (full_string == NULL) { + ret = ENOMEM; + goto done; + } + + len = strlen(full_string); + + errno = 0; + written = sss_atomic_write_s(fd, full_string, len); + if (written == -1) { + ret = errno; + DEBUG(SSSDBG_OP_FAILURE, ("writing to SELinux data file %s" + "failed [%d]: %s", tmp_path, ret, + strerror(ret))); + goto done; + } + + if (written != len) { + DEBUG(SSSDBG_OP_FAILURE, ("Expected to write %d bytes, wrote %d", + written, len)); + ret = EIO; + goto done; + } + + errno = 0; + if (rename(tmp_path, path) < 0) { + ret = errno; + } else { + ret = EOK; + } + close(fd); + fd = -1; + +done: + if (fd != -1) { + close(fd); + if (unlink(tmp_path) < 0) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Could not remove file [%s]", + tmp_path)); + } + } + + talloc_free(tmp_ctx); + return ret; +} + +static errno_t remove_selinux_login_file(const char *username) +{ + char *path; + errno_t ret; + + path = selogin_path(NULL, username); + if (!path) return ENOMEM; + + errno = 0; + ret = unlink(path); + if (ret < 0) { + ret = errno; + if (ret == ENOENT) { + /* Just return success if the file was not there */ + ret = EOK; + } else { + DEBUG(SSSDBG_OP_FAILURE, + ("Could not remove login file %s [%d]: %s\n", + path, ret, strerror(ret))); + } + } + + talloc_free(path); + return ret; +} + + /* A more generic request to gather all SELinux and HBAC rules. Updates * cache if necessary */ @@ -912,3 +1248,17 @@ ipa_get_selinux_recv(struct tevent_req *req, return EOK; } + +#else +/* Simply return success if HAVE_SELINUX_LOGIN_DIR is not defined. */ +void ipa_selinux_handler(struct be_req *be_req) +{ + struct be_ctx *be_ctx = be_req_get_be_ctx(be_req); + struct pam_data *pd; + + pd = talloc_get_type(be_req_get_data(be_req), struct pam_data); + + pd->pam_status = PAM_SUCCESS; + be_req_terminate(be_req, DP_ERR_OK, EOK, "Success"); +} +#endif diff --git a/src/providers/ipa/ipa_selinux.h b/src/providers/ipa/ipa_selinux.h index 60c2211..0f3fadd 100644 --- a/src/providers/ipa/ipa_selinux.h +++ b/src/providers/ipa/ipa_selinux.h @@ -27,6 +27,13 @@ #include "providers/ldap/ldap_common.h" +#ifdef HAVE_SELINUX_LOGIN_DIR + +#define ALL_SERVICES "*" +#define selogin_path(mem_ctx, username) \ + talloc_asprintf(mem_ctx, "%s/logins/%s", selinux_policy_root(), username) +#endif + struct ipa_selinux_ctx { struct ipa_id_ctx *id_ctx; diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index 69791a0..647f827 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -22,7 +22,6 @@ #include #include "util/util.h" -#include "util/sss_selinux.h" #include "util/auth_utils.h" #include "db/sysdb.h" #include "confdb/confdb.h" @@ -33,10 +32,6 @@ #include "responder/pam/pamsrv.h" #include "responder/pam/pam_helpers.h" #include "db/sysdb.h" -#include "db/sysdb_selinux.h" -#ifdef HAVE_SELINUX_LOGIN_DIR -#include -#endif enum pam_verbosity { PAM_VERBOSITY_NO_MESSAGES = 0, @@ -371,298 +366,6 @@ fail: return ret; } -#ifdef HAVE_SELINUX_LOGIN_DIR - -#define ALL_SERVICES "*" -#define selogin_path(mem_ctx, username) \ - talloc_asprintf(mem_ctx, "%s/logins/%s", selinux_policy_root(), username) - -static errno_t write_selinux_login_file(const char *username, char *string) -{ - char *path = NULL; - char *tmp_path = NULL; - ssize_t written; - int len; - int fd = -1; - mode_t oldmask; - TALLOC_CTX *tmp_ctx; - char *full_string = NULL; - int enforce; - errno_t ret = EOK; - - len = strlen(string); - if (len == 0) { - return EINVAL; - } - - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n")); - return ENOMEM; - } - - path = selogin_path(tmp_ctx, username); - if (path == NULL) { - ret = ENOMEM; - goto done; - } - - tmp_path = talloc_asprintf(tmp_ctx, "%sXXXXXX", path); - if (tmp_path == NULL) { - ret = ENOMEM; - goto done; - } - - oldmask = umask(022); - fd = mkstemp(tmp_path); - ret = errno; - umask(oldmask); - if (fd < 0) { - if (ret == ENOENT) { - /* if selinux is disabled and selogin dir does not exist, - * just ignore the error */ - if (selinux_getenforcemode(&enforce) == 0 && enforce == -1) { - ret = EOK; - goto done; - } - - /* continue if we can't get enforce mode or selinux is enabled */ - } - - DEBUG(SSSDBG_OP_FAILURE, ("unable to create temp file [%s] " - "for SELinux data [%d]: %s\n", tmp_path, ret, strerror(ret))); - goto done; - } - - full_string = talloc_asprintf(tmp_ctx, "%s:%s", ALL_SERVICES, string); - if (full_string == NULL) { - ret = ENOMEM; - goto done; - } - - len = strlen(full_string); - - errno = 0; - written = sss_atomic_write_s(fd, full_string, len); - if (written == -1) { - ret = errno; - DEBUG(SSSDBG_OP_FAILURE, ("writing to SELinux data file %s" - "failed [%d]: %s", tmp_path, ret, - strerror(ret))); - goto done; - } - - if (written != len) { - DEBUG(SSSDBG_OP_FAILURE, ("Expected to write %d bytes, wrote %d", - written, len)); - ret = EIO; - goto done; - } - - errno = 0; - if (rename(tmp_path, path) < 0) { - ret = errno; - } else { - ret = EOK; - } - close(fd); - fd = -1; - -done: - if (fd != -1) { - close(fd); - if (unlink(tmp_path) < 0) { - DEBUG(SSSDBG_MINOR_FAILURE, ("Could not remove file [%s]", - tmp_path)); - } - } - - talloc_free(tmp_ctx); - return ret; -} - -static errno_t remove_selinux_login_file(const char *username) -{ - char *path; - errno_t ret; - - path = selogin_path(NULL, username); - if (!path) return ENOMEM; - - errno = 0; - ret = unlink(path); - if (ret < 0) { - ret = errno; - if (ret == ENOENT) { - /* Just return success if the file was not there */ - ret = EOK; - } else { - DEBUG(SSSDBG_OP_FAILURE, - ("Could not remove login file %s [%d]: %s\n", - path, ret, strerror(ret))); - } - } - - talloc_free(path); - return ret; -} - -static errno_t process_selinux_mappings(struct pam_auth_req *preq) -{ - struct sysdb_ctx *sysdb; - TALLOC_CTX *tmp_ctx; - struct pam_data *pd = preq->pd; - char *file_content = NULL; - struct ldb_message **usermaps; - struct ldb_message *config; - const char *default_user = NULL; - const char *tmp_str; - char *order = NULL; - char **order_array; - errno_t ret, err; - int i, j; - size_t order_count; - size_t len = 0; - - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - ret = ENOMEM; - goto done; - } - - sysdb = preq->domain->sysdb; - if (sysdb == NULL) { - DEBUG(SSSDBG_FATAL_FAILURE, ("Fatal: Sysdb CTX not found for " - "domain [%s]!\n", preq->domain->name)); - ret = EINVAL; - goto done; - } - - ret = sysdb_search_selinux_config(tmp_ctx, sysdb, - preq->domain, NULL, &config); - if (ret == ENOENT) { - DEBUG(SSSDBG_TRACE_INTERNAL, ("No SELinux support found for the domain\n")); - ret = EOK; - goto done; - } else if (ret != EOK) { - goto done; - } - - default_user = ldb_msg_find_attr_as_string(config, - SYSDB_SELINUX_DEFAULT_USER, - NULL); - if (!default_user || default_user[0] == '\0') { - /* Skip creating the maps altogether if there is no default - * or empty default - */ - ret = EOK; - goto done; - } - - tmp_str = ldb_msg_find_attr_as_string(config, - SYSDB_SELINUX_DEFAULT_ORDER, - NULL); - if (tmp_str == NULL) { - DEBUG(SSSDBG_OP_FAILURE, ("No map order given!\n")); - ret = EINVAL; - goto done; - } - - order = talloc_strdup(tmp_ctx, tmp_str); - if (order == NULL) { - ret = ENOMEM; - goto done; - } - len = strlen(order); - - /* The "order" string contains one or more SELinux user records - * separated by $. Now we need to create an array of string from - * this one string. First find out how many elements in the array - * will be. This way only one alloc will be necessary for the array - */ - order_count = 1; - for (i = 0; i < len; i++) { - if (order[i] == '$') order_count++; - } - - order_array = talloc_array(tmp_ctx, char *, order_count); - if (order_array == NULL) { - ret = ENOMEM; - goto done; - } - - /* Now fill the array with pointers to the original string. Also - * use binary zeros to make multiple string out of the one. - */ - order_array[0] = order; - order_count = 1; - for (i = 0; i < len; i++) { - if (order[i] == '$') { - order[i] = '\0'; - order_array[order_count] = &order[i+1]; - order_count++; - } - } - - /* Fetch all maps applicable to the user who is currently logging in */ - ret = sysdb_search_selinux_usermap_by_username(tmp_ctx, sysdb, - preq->domain, pd->user, - &usermaps); - if (ret != EOK && ret != ENOENT) { - goto done; - } - - /* If no maps match, we'll use the default SELinux user from the - * config */ - file_content = talloc_strdup(tmp_ctx, default_user); - if (file_content == NULL) { - ret = ENOMEM; - goto done; - } - - /* Iterate through the order array and try to find SELinux users - * in fetched maps. The order array contains all SELinux users - * allowed in the domain in the same order they should appear - * in the SELinux config file. If any user from the order array - * is not in fetched user maps, it means it should not be allowed - * for the user who is just logging in. - * - * Right now we have empty content of the SELinux config file, - * we shall add only those SELinux users that are present both in - * the order array and user maps applicable to the user who is - * logging in. - */ - for (i = 0; i < order_count; i++) { - for (j = 0; usermaps[j] != NULL; j++) { - tmp_str = sss_selinux_map_get_seuser(usermaps[j]); - - if (tmp_str && !strcasecmp(tmp_str, order_array[i])) { - /* If file_content contained something, overwrite it. - * This record has higher priority. - */ - talloc_zfree(file_content); - file_content = talloc_strdup(tmp_ctx, tmp_str); - if (file_content == NULL) { - ret = ENOMEM; - goto done; - } - break; - } - } - } - - ret = write_selinux_login_file(pd->user, file_content); -done: - if (!file_content) { - err = remove_selinux_login_file(pd->user); - /* Don't overwrite original error condition if there was one */ - if (ret == EOK) ret = err; - } - talloc_free(tmp_ctx); - return ret; -} -#endif - static errno_t filter_responses(struct confdb_ctx *cdb, struct response_data *resp_list) { @@ -863,18 +566,6 @@ static void pam_reply(struct pam_auth_req *preq) return; } -#ifdef HAVE_SELINUX_LOGIN_DIR - if (pd->cmd == SSS_PAM_ACCT_MGMT && - pd->pam_status == PAM_SUCCESS) { - /* Try to fetch data from sysdb - * (auth already passed -> we should have them) */ - ret = process_selinux_mappings(preq); - if (ret != EOK) { - pd->pam_status = PAM_SYSTEM_ERR; - } - } -#endif - ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in), &cctx->creq->out); if (ret != EOK) { diff --git a/src/util/sss_selinux.c b/src/util/sss_selinux.c index ee5ddab..dcac9ee 100644 --- a/src/util/sss_selinux.c +++ b/src/util/sss_selinux.c @@ -239,16 +239,16 @@ done: return ret; } -const char *sss_selinux_map_get_seuser(struct ldb_message *usermap) +const char *sss_selinux_map_get_seuser(struct sysdb_attrs *usermap) { int i; const uint8_t *name; const uint8_t *template = (const uint8_t *)SYSDB_SELINUX_USER; - for (i = 0; i < usermap->num_elements; i++) { - name = (const uint8_t *)usermap->elements[i].name; + for (i = 0; i < usermap->num; i++) { + name = (const uint8_t *)usermap->a[i].name; if (sss_utf8_case_eq(name, template) == 0) { - return (const char *)usermap->elements[i].values[0].data; + return (const char *)usermap->a[i].values[0].data; } } diff --git a/src/util/sss_selinux.h b/src/util/sss_selinux.h index 5dae5dd..ae5b2f3 100644 --- a/src/util/sss_selinux.h +++ b/src/util/sss_selinux.h @@ -50,6 +50,6 @@ bool sss_selinux_match(struct sysdb_attrs *usermap, struct sysdb_attrs *host, uint32_t *_priority); -const char *sss_selinux_map_get_seuser(struct ldb_message *usermap); +const char *sss_selinux_map_get_seuser(struct sysdb_attrs *usermap); #endif /* SSS_SELINUX_H_ */