diff options
-rw-r--r-- | Makefile.am | 29 | ||||
-rw-r--r-- | contrib/sssd.spec.in | 3 | ||||
-rw-r--r-- | src/providers/files/files_id.c | 179 | ||||
-rw-r--r-- | src/providers/files/files_init.c | 92 | ||||
-rw-r--r-- | src/providers/files/files_ops.c | 801 | ||||
-rw-r--r-- | src/providers/files/files_private.h | 74 | ||||
-rw-r--r-- | src/tests/dlopen-tests.c | 2 |
7 files changed, 1179 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 09c021d22..e6d3530d0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -339,7 +339,8 @@ sssdlib_LTLIBRARIES = \ libsss_ldap.la \ libsss_krb5.la \ libsss_proxy.la \ - libsss_simple.la + libsss_simple.la \ + $(NULL) if BUILD_SAMBA sssdlib_LTLIBRARIES += \ @@ -347,6 +348,12 @@ sssdlib_LTLIBRARIES += \ libsss_ad.la endif +if HAVE_INOTIFY +sssdlib_LTLIBRARIES += \ + libsss_files.la \ + $(NULL) +endif # HAVE_INOTIFY + ldblib_LTLIBRARIES = \ memberof.la @@ -771,6 +778,7 @@ dist_noinst_HEADERS = \ src/providers/ad/ad_subdomains.h \ src/providers/proxy/proxy.h \ src/providers/proxy/proxy_iface_generated.h \ + src/providers/files/files_private.h \ src/tools/tools_util.h \ src/tools/sss_sync_ops.h \ src/resolv/async_resolv.h \ @@ -1853,6 +1861,7 @@ if BUILD_SEMANAGE FILES_TESTS_LIBS += $(SEMANAGE_LIBS) endif +if HAVE_INOTIFY files_tests_SOURCES = \ src/tests/files-tests.c \ src/util/check_and_open.c \ @@ -1866,6 +1875,7 @@ files_tests_LDADD = \ $(FILES_TESTS_LIBS) \ libsss_test_common.la \ $(SSSD_INTERNAL_LTLIBS) +endif # HAVE_INOTIFY SSSD_RESOLV_TESTS_OBJ = \ $(SSSD_RESOLV_OBJ) @@ -3510,6 +3520,23 @@ libsss_proxy_la_LDFLAGS = \ -avoid-version \ -module +libsss_files_la_SOURCES = \ + src/providers/files/files_init.c \ + src/providers/files/files_id.c \ + src/providers/files/files_ops.c \ + src/util/inotify.c \ + $(NULL) +libsss_files_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(NULL) +libsss_files_la_LIBADD = \ + $(PAM_LIBS) \ + $(NULL) +libsss_files_la_LDFLAGS = \ + -avoid-version \ + -module \ + $(NULL) + libsss_simple_la_SOURCES = \ src/providers/simple/simple_access_check.c \ src/providers/simple/simple_access.c diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in index c0c9e364b..fe51f9998 100644 --- a/contrib/sssd.spec.in +++ b/contrib/sssd.spec.in @@ -848,6 +848,9 @@ done %{_sbindir}/sss_cache %{_libexecdir}/%{servicename}/sss_signal +# The files provider is intentionally packaged in -common +%{_libdir}/%{name}/libsss_files.so + %dir %{sssdstatedir} %dir %{_localstatedir}/cache/krb5rcache %attr(700,sssd,sssd) %dir %{dbpath} diff --git a/src/providers/files/files_id.c b/src/providers/files/files_id.c new file mode 100644 index 000000000..41314c66b --- /dev/null +++ b/src/providers/files/files_id.c @@ -0,0 +1,179 @@ +/* + SSSD + + files_id.c - Identity operaions on the files provider + + Copyright (C) 2016 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 "providers/data_provider/dp.h" +#include "providers/files/files_private.h" + +struct files_account_info_handler_state { + struct dp_reply_std reply; + + struct files_id_ctx *id_ctx; +}; + +struct tevent_req * +files_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct files_id_ctx *id_ctx, + struct dp_id_data *data, + struct dp_req_params *params) +{ + struct files_account_info_handler_state *state; + struct tevent_req *req; + struct tevent_req **update_req = NULL; + bool needs_update; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct files_account_info_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + state->id_ctx = id_ctx; + + switch (data->entry_type & BE_REQ_TYPE_MASK) { + case BE_REQ_USER: + if (data->filter_type != BE_FILTER_ENUM) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unexpected user filter type: %d\n", data->filter_type); + ret = EINVAL; + goto immediate; + } + update_req = &id_ctx->users_req; + needs_update = id_ctx->updating_passwd ? true : false; + break; + case BE_REQ_GROUP: + if (data->filter_type != BE_FILTER_ENUM) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unexpected group filter type: %d\n", data->filter_type); + ret = EINVAL; + goto immediate; + } + update_req = &id_ctx->groups_req; + needs_update = id_ctx->updating_groups ? true : false; + break; + case BE_REQ_INITGROUPS: + if (data->filter_type != BE_FILTER_NAME) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unexpected initgr filter type: %d\n", data->filter_type); + ret = EINVAL; + goto immediate; + } + if (strcmp(data->filter_value, DP_REQ_OPT_FILES_INITGR) != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unexpected initgr filter value: %d\n", data->filter_type); + ret = EINVAL; + goto immediate; + } + update_req = &id_ctx->initgroups_req; + needs_update = id_ctx->updating_groups || id_ctx->updating_passwd \ + ? true \ + : false; + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, + "Unexpected entry type: %d\n", data->entry_type & BE_REQ_TYPE_MASK); + ret = EINVAL; + goto immediate; + } + + if (needs_update == false) { + DEBUG(SSSDBG_TRACE_LIBS, "The files domain no longer needs an update\n"); + ret = EOK; + goto immediate; + } + + if (*update_req != NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Received a concurrent update!\n"); + ret = EAGAIN; + goto immediate; + } + + /* id_ctx now must mark the requests as updated when the inotify-induced + * update finishes + */ + *update_req = req; + return req; + +immediate: + dp_reply_std_set(&state->reply, DP_ERR_DECIDE, ret, NULL); + + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + + tevent_req_post(req, params->ev); + return req; +} + +static void finish_update_req(struct tevent_req **update_req, + errno_t ret) +{ + if (*update_req == NULL) { + return; + } + + if (ret != EOK) { + tevent_req_error(*update_req, ret); + } else { + tevent_req_done(*update_req); + } + *update_req = NULL; +} + +void files_account_info_finished(struct files_id_ctx *id_ctx, + int req_type, + errno_t ret) +{ + switch (req_type) { + case BE_REQ_USER: + finish_update_req(&id_ctx->users_req, ret); + if (id_ctx->updating_groups == false) { + finish_update_req(&id_ctx->initgroups_req, ret); + } + break; + case BE_REQ_GROUP: + finish_update_req(&id_ctx->groups_req, ret); + if (id_ctx->updating_passwd == false) { + finish_update_req(&id_ctx->initgroups_req, ret); + } + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, + "Unexpected req_type %d\n", req_type); + return; + } +} + +errno_t files_account_info_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data) +{ + struct files_account_info_handler_state *state = NULL; + + state = tevent_req_data(req, struct files_account_info_handler_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *data = state->reply; + return EOK; +} diff --git a/src/providers/files/files_init.c b/src/providers/files/files_init.c new file mode 100644 index 000000000..b91dfbac9 --- /dev/null +++ b/src/providers/files/files_init.c @@ -0,0 +1,92 @@ +/* + SSSD + + files_init.c - Initialization of the files provider + + Copyright (C) 2016 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 "providers/data_provider/dp.h" +#include "providers/files/files_private.h" + +int sssm_files_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct data_provider *provider, + const char *module_name, + void **_module_data) +{ + struct files_id_ctx *ctx; + int ret; + const char *passwd_file = NULL; + const char *group_file = NULL; + + /* So far this is mostly useful for tests */ + passwd_file = getenv("SSS_FILES_PASSWD"); + if (passwd_file == NULL) { + passwd_file = "/etc/passwd"; + } + + group_file = getenv("SSS_FILES_GROUP"); + if (group_file == NULL) { + group_file = "/etc/group"; + } + + ctx = talloc_zero(mem_ctx, struct files_id_ctx); + if (ctx == NULL) { + return ENOMEM; + } + ctx->be = be_ctx; + ctx->domain = be_ctx->domain; + ctx->passwd_file = passwd_file; + ctx->group_file = group_file; + + ctx->fctx = sf_init(ctx, be_ctx->ev, + ctx->passwd_file, ctx->group_file, + ctx); + if (ctx->fctx == NULL) { + ret = ENOMEM; + goto done; + } + + *_module_data = ctx; + ret = EOK; +done: + if (ret != EOK) { + talloc_free(ctx); + } + return ret; +} + +int sssm_files_id_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + void *module_data, + struct dp_method *dp_methods) +{ + struct files_id_ctx *ctx; + + ctx = talloc_get_type(module_data, struct files_id_ctx); + if (ctx == NULL) { + return EINVAL; + } + + dp_set_method(dp_methods, DPM_ACCOUNT_HANDLER, + files_account_info_handler_send, + files_account_info_handler_recv, + ctx, struct files_id_ctx, + struct dp_id_data, struct dp_reply_std); + + return EOK; +} diff --git a/src/providers/files/files_ops.c b/src/providers/files/files_ops.c new file mode 100644 index 000000000..beda47abd --- /dev/null +++ b/src/providers/files/files_ops.c @@ -0,0 +1,801 @@ +/* + SSSD + + Files provider operations + + Copyright (C) 2016 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 <dlfcn.h> + +#include "config.h" + +#include "providers/files/files_private.h" +#include "db/sysdb.h" +#include "util/inotify.h" +#include "util/util.h" + +#define FILES_REALLOC_CHUNK 64 + +#define PWD_MAXSIZE 1024 +#define GRP_MAXSIZE 2048 + +struct files_ctx { + struct snotify_ctx *pwd_watch; + struct snotify_ctx *grp_watch; + + struct files_ops_ctx *ops; +}; + +static errno_t enum_files_users(TALLOC_CTX *mem_ctx, + struct files_id_ctx *id_ctx, + struct passwd ***_users) +{ + errno_t ret, close_ret; + struct passwd *pwd_iter = NULL; + struct passwd *pwd = NULL; + struct passwd **users = NULL; + FILE *pwd_handle = NULL; + size_t n_users = 0; + + pwd_handle = fopen(id_ctx->passwd_file, "r"); + if (pwd_handle == NULL) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot open passwd file %s [%d]\n", + id_ctx->passwd_file, ret); + goto done; + } + + users = talloc_zero_array(mem_ctx, struct passwd *, + FILES_REALLOC_CHUNK); + if (users == NULL) { + ret = ENOMEM; + goto done; + } + + while ((pwd_iter = fgetpwent(pwd_handle)) != NULL) { + /* FIXME - we might want to support paging of sorts to avoid allocating + * all users atop a memory context or only return users that differ from + * the local storage as a diff to minimize memory spikes + */ + DEBUG(SSSDBG_TRACE_LIBS, + "User found (%s, %s, %"SPRIuid", %"SPRIgid", %s, %s, %s)\n", + pwd_iter->pw_name, pwd_iter->pw_passwd, + pwd_iter->pw_uid, pwd_iter->pw_gid, + pwd_iter->pw_gecos, pwd_iter->pw_dir, + pwd_iter->pw_shell); + + pwd = talloc_zero(users, struct passwd); + if (pwd == NULL) { + ret = ENOMEM; + goto done; + } + + pwd->pw_uid = pwd_iter->pw_uid; + pwd->pw_gid = pwd_iter->pw_gid; + + pwd->pw_name = talloc_strdup(pwd, pwd_iter->pw_name); + if (pwd->pw_name == NULL) { + /* We only check pw_name here on purpose to allow broken + * records to be optionally rejected when saving them + * or fallback values to be used. + */ + ret = ENOMEM; + goto done; + } + + pwd->pw_dir = talloc_strdup(pwd, pwd_iter->pw_dir); + pwd->pw_gecos = talloc_strdup(pwd, pwd_iter->pw_gecos); + pwd->pw_shell = talloc_strdup(pwd, pwd_iter->pw_shell); + pwd->pw_passwd = talloc_strdup(pwd, pwd_iter->pw_passwd); + + users[n_users] = pwd; + n_users++; + if (n_users % FILES_REALLOC_CHUNK == 0) { + users = talloc_realloc(mem_ctx, + users, + struct passwd *, + talloc_get_size(users) + FILES_REALLOC_CHUNK); + if (users == NULL) { + ret = ENOMEM; + goto done; + } + } + } + + ret = EOK; + *_users = users; +done: + if (ret != EOK) { + talloc_free(users); + } + + if (pwd_handle) { + close_ret = fclose(pwd_handle); + if (close_ret != 0) { + close_ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot close passwd file %s [%d]\n", + id_ctx->passwd_file, close_ret); + } + } + return ret; +} + +static errno_t enum_files_groups(TALLOC_CTX *mem_ctx, + struct files_id_ctx *id_ctx, + struct group ***_groups) +{ + errno_t ret, close_ret; + struct group *grp_iter = NULL; + struct group *grp = NULL; + struct group **groups = NULL; + size_t n_groups = 0; + FILE *grp_handle = NULL; + + grp_handle = fopen(id_ctx->group_file, "r"); + if (grp_handle == NULL) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot open group file %s [%d]\n", + id_ctx->group_file, ret); + goto done; + } + + groups = talloc_zero_array(mem_ctx, struct group *, + FILES_REALLOC_CHUNK); + if (groups == NULL) { + ret = ENOMEM; + goto done; + } + + while ((grp_iter = fgetgrent(grp_handle)) != NULL) { + DEBUG(SSSDBG_TRACE_LIBS, + "Group found (%s, %"SPRIgid")\n", + grp_iter->gr_name, grp_iter->gr_gid); + + grp = talloc_zero(groups, struct group); + if (grp == NULL) { + ret = ENOMEM; + goto done; + } + + grp->gr_gid = grp_iter->gr_gid; + grp->gr_name = talloc_strdup(grp, grp_iter->gr_name); + if (grp->gr_name == NULL) { + /* We only check gr_name here on purpose to allow broken + * records to be optionally rejected when saving them + * or fallback values to be used. + */ + ret = ENOMEM; + goto done; + } + grp->gr_passwd = talloc_strdup(grp, grp_iter->gr_passwd); + + if (grp_iter->gr_mem != NULL && grp_iter->gr_mem[0] != '\0') { + size_t nmem; + + for (nmem = 0; grp_iter->gr_mem[nmem] != NULL; nmem++); + + grp->gr_mem = talloc_zero_array(grp, char *, nmem + 1); + if (grp->gr_mem == NULL) { + ret = ENOMEM; + goto done; + } + + for (nmem = 0; grp_iter->gr_mem[nmem] != NULL; nmem++) { + grp->gr_mem[nmem] = talloc_strdup(grp, grp_iter->gr_mem[nmem]); + if (grp->gr_mem[nmem] == NULL) { + ret = ENOMEM; + goto done; + } + } + } + + groups[n_groups] = grp; + n_groups++; + if (n_groups % FILES_REALLOC_CHUNK == 0) { + groups = talloc_realloc(mem_ctx, + groups, + struct group *, + talloc_get_size(groups) + FILES_REALLOC_CHUNK); + if (groups == NULL) { + ret = ENOMEM; + goto done; + } + } + } + + ret = EOK; + *_groups = groups; +done: + if (ret != EOK) { + talloc_free(groups); + } + + if (grp_handle) { + close_ret = fclose(grp_handle); + if (close_ret != 0) { + close_ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot close group file %s [%d]\n", + id_ctx->group_file, close_ret); + } + } + return ret; +} + +static errno_t delete_all_users(struct sss_domain_info *dom) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *base_dn; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + base_dn = sysdb_user_base_dn(tmp_ctx, dom); + if (base_dn == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n"); + ret = ENOMEM; + goto done; + } + + ret = sysdb_delete_recursive(dom->sysdb, base_dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to delete users subtree [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static errno_t save_file_user(struct files_id_ctx *id_ctx, + struct passwd *pw) +{ + errno_t ret; + char *fqname; + TALLOC_CTX *tmp_ctx = NULL; + const char *shell; + const char *gecos; + struct sysdb_attrs *attrs = NULL; + + if (strcmp(pw->pw_name, "root") == 0 + || pw->pw_uid == 0 + || pw->pw_gid == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "Skipping %s\n", pw->pw_name); + return EOK; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + fqname = sss_create_internal_fqname(tmp_ctx, pw->pw_name, + id_ctx->domain->name); + if (fqname == NULL) { + ret = ENOMEM; + goto done; + } + + attrs = sysdb_new_attrs(tmp_ctx); + if (attrs == NULL) { + ret = ENOMEM; + goto done; + } + + if (pw->pw_shell && pw->pw_shell[0] != '\0') { + shell = pw->pw_shell; + } else { + shell = NULL; + } + + if (pw->pw_gecos && pw->pw_gecos[0] != '\0') { + gecos = pw->pw_gecos; + } else { + gecos = NULL; + } + + /* FIXME - optimize later */ + ret = sysdb_store_user(id_ctx->domain, + fqname, + pw->pw_passwd, + pw->pw_uid, + pw->pw_gid, + gecos, + pw->pw_dir, + shell, + NULL, attrs, + NULL, 0, 0); + if (ret != EOK) { + goto done; + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t sf_enum_groups(struct files_id_ctx *id_ctx); + +errno_t sf_enum_users(struct files_id_ctx *id_ctx) +{ + errno_t ret; + errno_t tret; + TALLOC_CTX *tmp_ctx = NULL; + struct passwd **users = NULL; + bool in_transaction = false; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = enum_files_users(tmp_ctx, id_ctx, &users); + if (ret != EOK) { + goto done; + } + + ret = sysdb_transaction_start(id_ctx->domain->sysdb); + if (ret != EOK) { + goto done; + } + in_transaction = true; + + /* remove previous cache contents */ + /* FIXME - this is terribly inefficient */ + ret = delete_all_users(id_ctx->domain); + if (ret != EOK) { + goto done; + } + + for (size_t i = 0; users[i]; i++) { + ret = save_file_user(id_ctx, users[i]); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot save user %s: [%d]: %s\n", + users[i]->pw_name, ret, sss_strerror(ret)); + continue; + } + } + + ret = sysdb_transaction_commit(id_ctx->domain->sysdb); + if (ret != EOK) { + goto done; + } + in_transaction = false; + + /* Covers the case when someone edits /etc/group, adds a group member and + * only then edits passwd and adds the user. The reverse is not needed, + * because member/memberof links are established when groups are saved. + */ + ret = sf_enum_groups(id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot refresh groups\n"); + goto done; + } + + ret = EOK; +done: + if (in_transaction) { + tret = sysdb_transaction_cancel(id_ctx->domain->sysdb); + if (tret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot cancel transaction: %d\n", ret); + } + } + talloc_free(tmp_ctx); + return ret; +} + +static const char **get_cached_user_names(TALLOC_CTX *mem_ctx, + struct sss_domain_info *dom) +{ + errno_t ret; + struct ldb_result *res = NULL; + const char **user_names = NULL; + unsigned c = 0; + + ret = sysdb_enumpwent(mem_ctx, dom, &res); + if (ret != EOK) { + goto done; + } + + user_names = talloc_zero_array(mem_ctx, const char *, res->count + 1); + if (user_names == NULL) { + goto done; + } + + for (unsigned i = 0; i < res->count; i++) { + user_names[c] = ldb_msg_find_attr_as_string(res->msgs[i], + SYSDB_NAME, + NULL); + if (user_names[c] == NULL) { + continue; + } + c++; + } + +done: + /* Don't free res and keep it around to avoid duplicating the names */ + return user_names; +} + +static errno_t delete_all_groups(struct sss_domain_info *dom) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *base_dn; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + base_dn = sysdb_group_base_dn(tmp_ctx, dom); + if (base_dn == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n"); + ret = ENOMEM; + goto done; + } + + ret = sysdb_delete_recursive(dom->sysdb, base_dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to delete groups subtree [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static errno_t save_file_group(struct files_id_ctx *id_ctx, + struct group *grp, + const char **cached_users) +{ + errno_t ret; + char *fqname; + struct sysdb_attrs *attrs = NULL; + TALLOC_CTX *tmp_ctx = NULL; + char **fq_gr_files_mem; + const char **fq_gr_mem; + unsigned mi = 0; + + if (strcmp(grp->gr_name, "root") == 0 + || grp->gr_gid == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "Skipping %s\n", grp->gr_name); + return EOK; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + fqname = sss_create_internal_fqname(tmp_ctx, grp->gr_name, + id_ctx->domain->name); + if (fqname == NULL) { + ret = ENOMEM; + goto done; + + } + + attrs = sysdb_new_attrs(tmp_ctx); + if (attrs == NULL) { + ret = ENOMEM; + goto done; + } + + if (grp->gr_mem && grp->gr_mem[0]) { + fq_gr_files_mem = sss_create_internal_fqname_list( + tmp_ctx, + (const char *const*) grp->gr_mem, + id_ctx->domain->name); + if (fq_gr_files_mem == NULL) { + ret = ENOMEM; + goto done; + } + + fq_gr_mem = talloc_zero_array(tmp_ctx, const char *, + talloc_array_length(fq_gr_files_mem)); + if (fq_gr_mem == NULL) { + ret = ENOMEM; + goto done; + } + + for (unsigned i=0; fq_gr_files_mem[i] != NULL; i++) { + if (string_in_list(fq_gr_files_mem[i], + discard_const(cached_users), + true)) { + fq_gr_mem[mi] = fq_gr_files_mem[i]; + mi++; + + DEBUG(SSSDBG_TRACE_LIBS, + "User %s is cached, will become a member of %s\n", + fq_gr_files_mem[i], grp->gr_name); + } else { + ret = sysdb_attrs_add_string(attrs, + SYSDB_GHOST, + fq_gr_files_mem[i]); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot add ghost %s for group %s\n", + fq_gr_files_mem[i], fqname); + continue; + } + + DEBUG(SSSDBG_TRACE_LIBS, + "User %s is not cached, will become a ghost of %s\n", + fq_gr_files_mem[i], grp->gr_name); + } + } + + if (fq_gr_mem != NULL && fq_gr_mem[0] != NULL) { + ret = sysdb_attrs_users_from_str_list( + attrs, SYSDB_MEMBER, id_ctx->domain->name, + (const char *const *) fq_gr_mem); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, "Could not add group members\n"); + goto done; + } + } + + } + + ret = sysdb_store_group(id_ctx->domain, fqname, grp->gr_gid, + attrs, 0, 0); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, "Could not add group to cache\n"); + goto done; + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t sf_enum_groups(struct files_id_ctx *id_ctx) +{ + errno_t ret; + errno_t tret; + TALLOC_CTX *tmp_ctx = NULL; + struct group **groups = NULL; + bool in_transaction = false; + const char **cached_users = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + ret = enum_files_groups(tmp_ctx, id_ctx, &groups); + if (ret != EOK) { + goto done; + } + + cached_users = get_cached_user_names(tmp_ctx, id_ctx->domain); + if (cached_users == NULL) { + goto done; + } + + ret = sysdb_transaction_start(id_ctx->domain->sysdb); + if (ret != EOK) { + goto done; + } + in_transaction = true; + + /* remove previous cache contents */ + ret = delete_all_groups(id_ctx->domain); + if (ret != EOK) { + goto done; + } + + for (size_t i = 0; groups[i]; i++) { + ret = save_file_group(id_ctx, groups[i], cached_users); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot save group %s\n", groups[i]->gr_name); + continue; + } + } + + ret = sysdb_transaction_commit(id_ctx->domain->sysdb); + if (ret != EOK) { + goto done; + } + in_transaction = false; + + ret = EOK; +done: + if (in_transaction) { + tret = sysdb_transaction_cancel(id_ctx->domain->sysdb); + if (tret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot cancel transaction: %d\n", ret); + } + } + talloc_free(tmp_ctx); + return ret; +} + +static void sf_cb_done(struct files_id_ctx *id_ctx) +{ + /* Only activate a domain when both callbacks are done */ + if (id_ctx->updating_passwd == false + && id_ctx->updating_groups == false) { + dp_sbus_domain_active(id_ctx->be->provider, + id_ctx->domain); + } +} + +static int sf_passwd_cb(const char *filename, uint32_t flags, void *pvt) +{ + struct files_id_ctx *id_ctx; + errno_t ret; + + id_ctx = talloc_get_type(pvt, struct files_id_ctx); + if (id_ctx == NULL) { + return EINVAL; + } + + DEBUG(SSSDBG_TRACE_FUNC, "passwd notification\n"); + + if (strcmp(filename, id_ctx->passwd_file) != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Wrong file, expected %s, got %s\n", + id_ctx->passwd_file, filename); + return EINVAL; + } + + id_ctx->updating_passwd = true; + dp_sbus_domain_inconsistent(id_ctx->be->provider, id_ctx->domain); + + dp_sbus_reset_users_ncache(id_ctx->be->provider, id_ctx->domain); + dp_sbus_reset_users_memcache(id_ctx->be->provider); + dp_sbus_reset_initgr_memcache(id_ctx->be->provider); + + ret = sf_enum_users(id_ctx); + + id_ctx->updating_passwd = false; + sf_cb_done(id_ctx); + files_account_info_finished(id_ctx, BE_REQ_USER, ret); + return ret; +} + +static int sf_group_cb(const char *filename, uint32_t flags, void *pvt) +{ + struct files_id_ctx *id_ctx; + errno_t ret; + + id_ctx = talloc_get_type(pvt, struct files_id_ctx); + if (id_ctx == NULL) { + return EINVAL; + } + + DEBUG(SSSDBG_TRACE_FUNC, "group notification\n"); + + if (strcmp(filename, id_ctx->group_file) != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Wrong file, expected %s, got %s\n", + id_ctx->group_file, filename); + return EINVAL; + } + + id_ctx->updating_groups = true; + dp_sbus_domain_inconsistent(id_ctx->be->provider, id_ctx->domain); + + dp_sbus_reset_groups_ncache(id_ctx->be->provider, id_ctx->domain); + dp_sbus_reset_groups_memcache(id_ctx->be->provider); + dp_sbus_reset_initgr_memcache(id_ctx->be->provider); + + ret = sf_enum_groups(id_ctx); + + id_ctx->updating_groups = false; + sf_cb_done(id_ctx); + files_account_info_finished(id_ctx, BE_REQ_GROUP, ret); + return ret; +} + +static void startup_enum_files(struct tevent_context *ev, + struct tevent_immediate *imm, + void *pvt) +{ + struct files_id_ctx *id_ctx = talloc_get_type(pvt, struct files_id_ctx); + errno_t ret; + + talloc_zfree(imm); + + ret = sf_enum_users(id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Enumerating users failed, data might be inconsistent!\n"); + } + + ret = sf_enum_groups(id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Enumerating groups failed, data might be inconsistent!\n"); + } +} + +static struct snotify_ctx *sf_setup_watch(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *filename, + snotify_cb_fn fn, + struct files_id_ctx *id_ctx) +{ + return snotify_create(mem_ctx, ev, SNOTIFY_WATCH_DIR, + filename, NULL, + IN_DELETE_SELF | IN_CLOSE_WRITE | IN_MOVE_SELF | \ + IN_CREATE | IN_MOVED_TO, + fn, id_ctx); +} + +struct files_ctx *sf_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *passwd_file, + const char *group_file, + struct files_id_ctx *id_ctx) +{ + struct files_ctx *fctx; + struct tevent_immediate *imm; + + fctx = talloc(mem_ctx, struct files_ctx); + if (fctx == NULL) { + return NULL; + } + + fctx->pwd_watch = sf_setup_watch(fctx, ev, passwd_file, + sf_passwd_cb, id_ctx); + fctx->grp_watch = sf_setup_watch(fctx, ev, group_file, + sf_group_cb, id_ctx); + if (fctx->pwd_watch == NULL || fctx->grp_watch == NULL) { + talloc_free(fctx); + return NULL; + } + + /* Enumerate users and groups on startup to process any changes when + * sssd was down. We schedule a request here to minimize the time + * we spend in the init function + */ + imm = tevent_create_immediate(id_ctx); + if (imm == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "tevent_create_immediate failed.\n"); + talloc_free(fctx); + return NULL; + } + tevent_schedule_immediate(imm, ev, startup_enum_files, id_ctx); + + return fctx; +} diff --git a/src/providers/files/files_private.h b/src/providers/files/files_private.h new file mode 100644 index 000000000..a7d195c90 --- /dev/null +++ b/src/providers/files/files_private.h @@ -0,0 +1,74 @@ +/* + SSSD + + Files provider declarations + + Copyright (C) 2016 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/>. +*/ + +#ifndef __FILES_PRIVATE_H_ +#define __FILES_PRIVATE_H_ + +#include "config.h" + +#include <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <sys/types.h> +#include <nss.h> +#include <pwd.h> +#include <grp.h> + +#include "providers/data_provider/dp.h" + +struct files_id_ctx { + struct be_ctx *be; + struct sss_domain_info *domain; + struct files_ctx *fctx; + + const char *passwd_file; + const char *group_file; + + bool updating_passwd; + bool updating_groups; + + struct tevent_req *users_req; + struct tevent_req *groups_req; + struct tevent_req *initgroups_req; +}; + +/* files_ops.c */ +struct files_ctx *sf_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *passwd_file, + const char *group_file, + struct files_id_ctx *id_ctx); + +/* files_id.c */ +struct tevent_req * +files_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct files_id_ctx *id_ctx, + struct dp_id_data *data, + struct dp_req_params *params); + +errno_t files_account_info_handler_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct dp_reply_std *data); + +void files_account_info_finished(struct files_id_ctx *id_ctx, + int req_type, + errno_t ret); +#endif /* __FILES_PRIVATE_H_ */ diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c index d11ae9651..419857cc7 100644 --- a/src/tests/dlopen-tests.c +++ b/src/tests/dlopen-tests.c @@ -80,6 +80,8 @@ struct so { { "libsss_util.so", { LIBPFX"libsss_util.so", NULL } }, { "libsss_simple.so", { LIBPFX"libdlopen_test_providers.so", LIBPFX"libsss_simple.so", NULL } }, + { "libsss_files.so", { LIBPFX"libdlopen_test_providers.so", + LIBPFX"libsss_files.so", NULL } }, #ifdef BUILD_SAMBA { "libsss_ad.so", { LIBPFX"libdlopen_test_providers.so", LIBPFX"libsss_ad.so", NULL } }, |