summaryrefslogtreecommitdiffstats
path: root/src/responder/common/negcache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/responder/common/negcache.c')
-rw-r--r--src/responder/common/negcache.c1417
1 files changed, 1417 insertions, 0 deletions
diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c
new file mode 100644
index 0000000..9ed0196
--- /dev/null
+++ b/src/responder/common/negcache.c
@@ -0,0 +1,1417 @@
+/*
+ SSSD
+
+ NSS Responder
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ 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 <fcntl.h>
+#include <time.h>
+#include "tdb.h"
+#include "util/util.h"
+#include "util/nss_dl_load.h"
+#include "confdb/confdb.h"
+#include "responder/common/negcache_files.h"
+#include "responder/common/responder.h"
+#include "responder/common/negcache.h"
+
+
+#define NC_ENTRY_PREFIX "NCE/"
+#define NC_USER_PREFIX NC_ENTRY_PREFIX"USER"
+#define NC_GROUP_PREFIX NC_ENTRY_PREFIX"GROUP"
+#define NC_NETGROUP_PREFIX NC_ENTRY_PREFIX"NETGR"
+#define NC_SERVICE_PREFIX NC_ENTRY_PREFIX"SERVICE"
+#define NC_UID_PREFIX NC_ENTRY_PREFIX"UID"
+#define NC_GID_PREFIX NC_ENTRY_PREFIX"GID"
+#define NC_SID_PREFIX NC_ENTRY_PREFIX"SID"
+#define NC_CERT_PREFIX NC_ENTRY_PREFIX"CERT"
+#define NC_DOMAIN_ACCT_LOCATE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE"
+#define NC_DOMAIN_ACCT_LOCATE_TYPE_PREFIX NC_ENTRY_PREFIX"DOM_LOCATE_TYPE"
+
+struct sss_nc_ctx {
+ struct tdb_context *tdb;
+ uint32_t timeout;
+ uint32_t local_timeout;
+ struct sss_nss_ops ops;
+};
+
+typedef int (*ncache_set_byname_fn_t)(struct sss_nc_ctx *, bool,
+ const char *, const char *);
+
+static int sss_ncache_set_ent(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name,
+ ncache_set_byname_fn_t setter);
+
+static int string_to_tdb_data(char *str, TDB_DATA *ret)
+{
+ if (!str || !ret) return EINVAL;
+
+ ret->dptr = (uint8_t *)str;
+ ret->dsize = strlen(str)+1;
+
+ return EOK;
+}
+
+static errno_t ncache_load_nss_symbols(struct sss_nss_ops *ops)
+{
+ errno_t ret;
+ struct sss_nss_symbols syms[] = {
+ {(void*)&ops->getpwnam_r, true, "getpwnam_r" },
+ {(void*)&ops->getpwuid_r, true, "getpwuid_r" },
+ {(void*)&ops->getgrnam_r, true, "getgrnam_r" },
+ {(void*)&ops->getgrgid_r, true, "getgrgid_r" }
+ };
+ size_t nsyms = sizeof(syms) / sizeof(struct sss_nss_symbols);
+
+ ret = sss_load_nss_symbols(ops, "files", syms, nsyms);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+int sss_ncache_init(TALLOC_CTX *memctx, uint32_t timeout,
+ uint32_t local_timeout, struct sss_nc_ctx **_ctx)
+{
+ errno_t ret;
+ struct sss_nc_ctx *ctx;
+
+ ctx = talloc_zero(memctx, struct sss_nc_ctx);
+ if (!ctx) return ENOMEM;
+
+ ret = ncache_load_nss_symbols(&ctx->ops);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to load NSS symbols [%d]: %s\n",
+ ret, sss_strerror(ret));
+ talloc_free(ctx);
+ return ret;
+ }
+
+ errno = 0;
+ /* open a memory only tdb with default hash size */
+ ctx->tdb = tdb_open("memcache", 0, TDB_INTERNAL, O_RDWR|O_CREAT, 0);
+ if (!ctx->tdb) return errno;
+
+ ctx->timeout = timeout;
+ ctx->local_timeout = local_timeout;
+
+ *_ctx = ctx;
+ return EOK;
+};
+
+uint32_t sss_ncache_get_timeout(struct sss_nc_ctx *ctx)
+{
+ return ctx->timeout;
+}
+
+static int sss_ncache_check_str(struct sss_nc_ctx *ctx, char *str)
+{
+ TDB_DATA key;
+ TDB_DATA data;
+ unsigned long long int timestamp;
+ bool expired = false;
+ char *ep;
+ int ret;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Checking negative cache for [%s]\n", str);
+
+ data.dptr = NULL;
+
+ ret = string_to_tdb_data(str, &key);
+ if (ret != EOK) goto done;
+
+ data = tdb_fetch(ctx->tdb, key);
+
+ if (!data.dptr) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ errno = 0;
+ timestamp = strtoull((const char *)data.dptr, &ep, 10);
+ if (errno != 0 || *ep != '\0') {
+ /* Malformed entry, remove it and return no entry */
+ expired = true;
+ goto done;
+ }
+
+ if (timestamp == 0) {
+ /* a 0 timestamp means this is a permanent entry */
+ ret = EEXIST;
+ goto done;
+ }
+
+ if (timestamp >= time(NULL)) {
+ /* still valid */
+ ret = EEXIST;
+ goto done;
+ }
+
+ expired = true;
+
+done:
+ if (expired) {
+ /* expired, remove and return no entry */
+ tdb_delete(ctx->tdb, key);
+ ret = ENOENT;
+ }
+
+ free(data.dptr);
+ return ret;
+}
+
+static int sss_ncache_set_str(struct sss_nc_ctx *ctx, char *str,
+ bool permanent, bool use_local_negative)
+{
+ TDB_DATA key;
+ TDB_DATA data;
+ char *timest;
+ unsigned long long int timell;
+ int ret;
+
+ ret = string_to_tdb_data(str, &key);
+ if (ret != EOK) return ret;
+
+ if (permanent) {
+ timest = talloc_strdup(ctx, "0");
+ } else {
+ if (use_local_negative == true && ctx->local_timeout > ctx->timeout) {
+ timell = ctx->local_timeout;
+ } else {
+ /* EOK is tested in cwrap based unit test */
+ if (ctx->timeout == 0) {
+ return EOK;
+ }
+ timell = ctx->timeout;
+ }
+ timell += (unsigned long long int)time(NULL);
+ timest = talloc_asprintf(ctx, "%llu", timell);
+ }
+ if (!timest) return ENOMEM;
+
+ ret = string_to_tdb_data(timest, &data);
+ if (ret != EOK) goto done;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Adding [%s] to negative cache%s\n",
+ str, permanent?" permanently":"");
+
+ ret = tdb_store(ctx->tdb, key, data, TDB_REPLACE);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Negative cache failed to set entry: [%s]\n",
+ tdb_errorstr(ctx->tdb));
+ ret = EFAULT;
+ }
+
+done:
+ talloc_free(timest);
+ return ret;
+}
+
+static int sss_ncache_check_user_int(struct sss_nc_ctx *ctx, const char *domain,
+ const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_check_group_int(struct sss_nc_ctx *ctx,
+ const char *domain, const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_check_netgr_int(struct sss_nc_ctx *ctx,
+ const char *domain, const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_check_service_int(struct sss_nc_ctx *ctx,
+ const char *domain,
+ const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s",
+ NC_SERVICE_PREFIX,
+ domain,
+ name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+typedef int (*ncache_check_byname_fn_t)(struct sss_nc_ctx *, const char *,
+ const char *);
+
+static int sss_cache_check_ent(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom, const char *name,
+ ncache_check_byname_fn_t checker)
+{
+ char *lower;
+ errno_t ret;
+
+ if (dom->case_sensitive == false) {
+ lower = sss_tc_utf8_str_tolower(ctx, name);
+ if (!lower) return ENOMEM;
+ ret = checker(ctx, dom->name, lower);
+ talloc_free(lower);
+ } else {
+ ret = checker(ctx, dom->name, name);
+ }
+
+ return ret;
+}
+
+int sss_ncache_check_user(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name)
+{
+ return sss_cache_check_ent(ctx, dom, name, sss_ncache_check_user_int);
+}
+
+int sss_ncache_check_upn(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name)
+{
+ char *neg_cache_name = NULL;
+ errno_t ret;
+
+ neg_cache_name = talloc_asprintf(ctx, "@%s", name);
+ if (neg_cache_name == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_cache_check_ent(ctx, dom, neg_cache_name,
+ sss_ncache_check_user_int);
+ talloc_free(neg_cache_name);
+
+ return ret;
+}
+
+int sss_ncache_check_group(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name)
+{
+ return sss_cache_check_ent(ctx, dom, name, sss_ncache_check_group_int);
+}
+
+int sss_ncache_check_netgr(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *name)
+{
+ return sss_cache_check_ent(ctx, dom, name, sss_ncache_check_netgr_int);
+}
+
+static int sss_ncache_set_service_int(struct sss_nc_ctx *ctx, bool permanent,
+ const char *domain, const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_SERVICE_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_set_str(ctx, str, permanent, false);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_service_name(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom,
+ const char *name, const char *proto)
+{
+ int ret;
+ char *service_and_protocol = talloc_asprintf(ctx, "%s:%s",
+ name,
+ proto ? proto : "<ANY>");
+ if (!service_and_protocol) return ENOMEM;
+
+ ret = sss_ncache_set_ent(ctx, permanent, dom,
+ service_and_protocol,
+ sss_ncache_set_service_int);
+ talloc_free(service_and_protocol);
+ return ret;
+}
+
+int sss_ncache_check_service(struct sss_nc_ctx *ctx,struct sss_domain_info *dom,
+ const char *name, const char *proto)
+{
+ int ret;
+ char *service_and_protocol = talloc_asprintf(ctx, "%s:%s",
+ name,
+ proto ? proto : "<ANY>");
+ if (!service_and_protocol) return ENOMEM;
+
+ ret = sss_cache_check_ent(ctx, dom, service_and_protocol,
+ sss_ncache_check_service_int);
+ talloc_free(service_and_protocol);
+ return ret;
+}
+
+int sss_ncache_set_service_port(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom,
+ uint16_t port, const char *proto)
+{
+ int ret;
+ char *service_and_protocol = talloc_asprintf(ctx, "%ul:%s",
+ port,
+ proto ? proto : "<ANY>");
+ if (!service_and_protocol) return ENOMEM;
+
+ ret = sss_ncache_set_ent(ctx, permanent, dom,
+ service_and_protocol,
+ sss_ncache_set_service_int);
+ talloc_free(service_and_protocol);
+ return ret;
+}
+
+int sss_ncache_check_service_port(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uint16_t port,
+ const char *proto)
+{
+ int ret;
+ char *service_and_protocol = talloc_asprintf(ctx, "%ul:%s",
+ port,
+ proto ? proto : "<ANY>");
+ if (!service_and_protocol) return ENOMEM;
+
+ ret = sss_cache_check_ent(ctx, dom, service_and_protocol,
+ sss_ncache_check_service_int);
+ talloc_free(service_and_protocol);
+ return ret;
+}
+
+
+
+int sss_ncache_check_uid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ uid_t uid)
+{
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%"SPRIuid, NC_UID_PREFIX, dom->name,
+ uid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%"SPRIuid, NC_UID_PREFIX, uid);
+ }
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_gid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ gid_t gid)
+{
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%"SPRIgid, NC_GID_PREFIX, dom->name,
+ gid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%"SPRIgid, NC_GID_PREFIX, gid);
+ }
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_sid(struct sss_nc_ctx *ctx, struct sss_domain_info *dom,
+ const char *sid)
+{
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_SID_PREFIX, dom->name, sid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%s", NC_SID_PREFIX, sid);
+ }
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert)
+{
+ char *str;
+ int ret;
+
+ str = talloc_asprintf(ctx, "%s/%s", NC_CERT_PREFIX, cert);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+
+static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent,
+ const char *domain, const char *name)
+{
+ bool use_local_negative = false;
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ if ((!permanent) && (ctx->local_timeout > 0)) {
+ use_local_negative = is_user_local_by_name(&ctx->ops, name);
+ }
+ ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_set_group_int(struct sss_nc_ctx *ctx, bool permanent,
+ const char *domain, const char *name)
+{
+ bool use_local_negative = false;
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ if ((!permanent) && (ctx->local_timeout > 0)) {
+ use_local_negative = is_group_local_by_name(&ctx->ops, name);
+ }
+ ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_set_netgr_int(struct sss_nc_ctx *ctx, bool permanent,
+ const char *domain, const char *name)
+{
+ char *str;
+ int ret;
+
+ if (!name || !*name) return EINVAL;
+
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_set_str(ctx, str, permanent, false);
+
+ talloc_free(str);
+ return ret;
+}
+
+static int sss_ncache_set_ent(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name,
+ ncache_set_byname_fn_t setter)
+{
+ char *lower;
+ errno_t ret;
+
+ if (dom->case_sensitive == false) {
+ lower = sss_tc_utf8_str_tolower(ctx, name);
+ if (!lower) return ENOMEM;
+ ret = setter(ctx, permanent, dom->name, lower);
+ talloc_free(lower);
+ } else {
+ ret = setter(ctx, permanent, dom->name, name);
+ }
+
+ return ret;
+}
+
+
+int sss_ncache_set_user(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name)
+{
+ return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_user_int);
+}
+
+int sss_ncache_set_upn(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name)
+{
+ char *neg_cache_name = NULL;
+ errno_t ret;
+
+ neg_cache_name = talloc_asprintf(ctx, "@%s", name);
+ if (neg_cache_name == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_set_ent(ctx, permanent, dom, neg_cache_name,
+ sss_ncache_set_user_int);
+ talloc_free(neg_cache_name);
+
+ return ret;
+}
+
+int sss_ncache_set_group(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name)
+{
+ return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_group_int);
+}
+
+int sss_ncache_set_netgr(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *name)
+{
+ return sss_ncache_set_ent(ctx, permanent, dom, name, sss_ncache_set_netgr_int);
+}
+
+int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, uid_t uid)
+{
+ bool use_local_negative = false;
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%"SPRIuid, NC_UID_PREFIX, dom->name,
+ uid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%"SPRIuid, NC_UID_PREFIX, uid);
+ }
+ if (!str) return ENOMEM;
+
+ if ((!permanent) && (ctx->local_timeout > 0)) {
+ use_local_negative = is_user_local_by_uid(&ctx->ops, uid);
+ }
+ ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, gid_t gid)
+{
+ bool use_local_negative = false;
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%"SPRIgid, NC_GID_PREFIX, dom->name,
+ gid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%"SPRIgid, NC_GID_PREFIX, gid);
+ }
+ if (!str) return ENOMEM;
+
+ if ((!permanent) && (ctx->local_timeout > 0)) {
+ use_local_negative = is_group_local_by_gid(&ctx->ops, gid);
+ }
+ ret = sss_ncache_set_str(ctx, str, permanent, use_local_negative);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_sid(struct sss_nc_ctx *ctx, bool permanent,
+ struct sss_domain_info *dom, const char *sid)
+{
+ char *str;
+ int ret;
+
+ if (dom != NULL) {
+ str = talloc_asprintf(ctx, "%s/%s/%s", NC_SID_PREFIX, dom->name, sid);
+ } else {
+ str = talloc_asprintf(ctx, "%s/%s", NC_SID_PREFIX, sid);
+ }
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_set_str(ctx, str, permanent, false);
+
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_cert(struct sss_nc_ctx *ctx, bool permanent,
+ const char *cert)
+{
+ char *str;
+ int ret;
+
+ str = talloc_asprintf(ctx, "%s/%s", NC_CERT_PREFIX, cert);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_set_str(ctx, str, permanent, false);
+
+ talloc_free(str);
+ return ret;
+}
+
+static char *domain_lookup_type_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *lookup_type)
+{
+ return talloc_asprintf(mem_ctx,
+ "%s/%s/%s",
+ NC_DOMAIN_ACCT_LOCATE_TYPE_PREFIX,
+ dom->name,
+ lookup_type);
+}
+
+int sss_ncache_set_domain_locate_type(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *lookup_type)
+{
+ char *str;
+ int ret;
+
+ str = domain_lookup_type_str(ctx, dom, lookup_type);
+ if (!str) return ENOMEM;
+
+ /* Permanent cache is always used here, because the lookup
+ * type's (getgrgid, getpwuid, ..) support locating an entry's domain
+ * doesn't change
+ */
+ ret = sss_ncache_set_str(ctx, str, true, false);
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_domain_locate_type(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *lookup_type)
+{
+ char *str;
+ int ret;
+
+ str = domain_lookup_type_str(ctx, dom, lookup_type);
+ if (!str) return ENOMEM;
+
+ ret = sss_ncache_check_str(ctx, str);
+ talloc_free(str);
+ return ret;
+}
+
+static char *locate_gid_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ gid_t gid)
+{
+ return talloc_asprintf(mem_ctx,
+ "%s/%s/%s/%"SPRIgid,
+ NC_DOMAIN_ACCT_LOCATE_PREFIX,
+ NC_GID_PREFIX,
+ dom->name,
+ gid);
+}
+
+int sss_ncache_set_locate_gid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ gid_t gid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_gid_str(ctx, dom, gid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_set_str(ctx, str, false, false);
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_locate_gid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ gid_t gid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_gid_str(ctx, dom, gid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_check_str(ctx, str);
+ talloc_free(str);
+ return ret;
+}
+
+static char *locate_uid_str(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uid_t uid)
+{
+ return talloc_asprintf(ctx,
+ "%s/%s/%s/%"SPRIuid,
+ NC_DOMAIN_ACCT_LOCATE_PREFIX,
+ NC_UID_PREFIX,
+ dom->name,
+ uid);
+}
+
+int sss_ncache_set_locate_uid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uid_t uid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_uid_str(ctx, dom, uid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_set_str(ctx, str, false, false);
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_check_locate_uid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ uid_t uid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_uid_str(ctx, dom, uid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_check_str(ctx, str);
+ talloc_free(str);
+ return ret;
+}
+
+static char *locate_sid_str(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *sid)
+{
+ return talloc_asprintf(ctx,
+ "%s/%s/%s/%s",
+ NC_DOMAIN_ACCT_LOCATE_PREFIX,
+ NC_SID_PREFIX,
+ dom->name,
+ sid);
+}
+
+int sss_ncache_check_locate_sid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *sid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_sid_str(ctx, dom, sid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_check_str(ctx, str);
+ talloc_free(str);
+ return ret;
+}
+
+int sss_ncache_set_locate_sid(struct sss_nc_ctx *ctx,
+ struct sss_domain_info *dom,
+ const char *sid)
+{
+ char *str;
+ int ret;
+
+ if (dom == NULL) {
+ return EINVAL;
+ }
+
+ str = locate_sid_str(ctx, dom, sid);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ncache_set_str(ctx, str, false, false);
+ talloc_free(str);
+ return ret;
+}
+
+static int delete_permanent(struct tdb_context *tdb,
+ TDB_DATA key, TDB_DATA data, void *state)
+{
+ unsigned long long int timestamp;
+ bool remove_key = false;
+ char *ep;
+
+ if (strncmp((char *)key.dptr,
+ NC_ENTRY_PREFIX, sizeof(NC_ENTRY_PREFIX) - 1) != 0) {
+ /* not interested in this key */
+ return 0;
+ }
+
+ errno = 0;
+ timestamp = strtoull((const char *)data.dptr, &ep, 10);
+ if (errno != 0 || *ep != '\0') {
+ /* Malformed entry, remove it */
+ remove_key = true;
+ goto done;
+ }
+
+ if (timestamp == 0) {
+ /* a 0 timestamp means this is a permanent entry */
+ remove_key = true;
+ }
+
+done:
+ if (remove_key) {
+ return tdb_delete(tdb, key);
+ }
+
+ return 0;
+}
+
+int sss_ncache_reset_permanent(struct sss_nc_ctx *ctx)
+{
+ int ret;
+
+ ret = tdb_traverse(ctx->tdb, delete_permanent, NULL);
+ if (ret < 0)
+ return EIO;
+
+ return EOK;
+}
+
+static int delete_prefix(struct tdb_context *tdb,
+ TDB_DATA key, TDB_DATA data, void *state)
+{
+ const char *prefix = (const char *) state;
+ unsigned long long int timestamp;
+ char *ep = NULL;
+
+ if (strncmp((char *)key.dptr, prefix, strlen(prefix) - 1) != 0) {
+ /* not interested in this key */
+ return 0;
+ }
+
+ errno = 0;
+ timestamp = strtoull((const char *)data.dptr, &ep, 10);
+ if ((errno == 0) && (*ep == '\0') && (timestamp == 0)) {
+ /* skip permanent entries */
+ return 0;
+ }
+
+ return tdb_delete(tdb, key);
+}
+
+static int sss_ncache_reset_pfx(struct sss_nc_ctx *ctx,
+ const char **prefixes)
+{
+ int ret;
+
+ if (prefixes == NULL) {
+ return EOK;
+ }
+
+ for (int i = 0; prefixes[i] != NULL; i++) {
+ ret = tdb_traverse(ctx->tdb,
+ delete_prefix,
+ discard_const(prefixes[i]));
+ if (ret < 0) {
+ return EIO;
+ }
+ }
+
+ return EOK;
+}
+
+int sss_ncache_reset_users(struct sss_nc_ctx *ctx)
+{
+ const char *prefixes[] = {
+ NC_USER_PREFIX,
+ NC_UID_PREFIX,
+ NULL,
+ };
+
+ return sss_ncache_reset_pfx(ctx, prefixes);
+}
+
+int sss_ncache_reset_groups(struct sss_nc_ctx *ctx)
+{
+ const char *prefixes[] = {
+ NC_GROUP_PREFIX,
+ NC_GID_PREFIX,
+ NULL,
+ };
+
+ return sss_ncache_reset_pfx(ctx, prefixes);
+}
+
+errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache,
+ struct confdb_ctx *cdb,
+ struct resp_ctx *rctx)
+{
+ errno_t ret;
+ char **filter_list = NULL;
+ char **default_list = NULL;
+ char *name = NULL;
+ struct sss_domain_info *dom = NULL;
+ struct sss_domain_info *domain_list = rctx->domains;
+ struct sss_domain_info *ddom;
+ char *domainname = NULL;
+ char *conf_path = NULL;
+ TALLOC_CTX *tmpctx = talloc_new(NULL);
+ int i;
+ char *fqname = NULL;
+
+ if (tmpctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Populate domain-specific negative cache user entries */
+ for (dom = domain_list; dom; dom = get_next_domain(dom, 0)) {
+ conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL,
+ dom->name);
+ if (!conf_path) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(filter_list);
+ ret = confdb_get_string_as_list(cdb, tmpctx, conf_path,
+ CONFDB_NSS_FILTER_USERS,
+ &filter_list);
+ if (ret == ENOENT) continue;
+ if (ret != EOK) goto done;
+
+ for (i = 0; (filter_list && filter_list[i]); i++) {
+ ret = sss_parse_name_for_domains(tmpctx, domain_list,
+ NULL,
+ filter_list[i],
+ &domainname, &name);
+ if (ret == EAGAIN) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Can add [%s] only as UPN to negcache because the "
+ "required domain is not known yet\n", filter_list[i]);
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid name in filterUsers list: [%s] (%d)\n",
+ filter_list[i], ret);
+ continue;
+ }
+
+ /* Check domain and its sub-domains */
+ for (ddom = dom; ddom != NULL;
+ ddom = get_next_domain(ddom, SSS_GND_ALL_SUBDOMAINS)) {
+
+ if (domainname && strcmp(domainname, ddom->name)) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Mismatch between domain name (%s) and name "
+ "set in FQN (%s), assuming %s is UPN\n",
+ ddom->name, domainname, filter_list[i]);
+ ret = sss_ncache_set_upn(ncache, true, ddom, filter_list[i]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_ncache_set_upn failed (%d [%s]), ignored\n",
+ ret, sss_strerror(ret));
+ }
+ continue;
+ }
+
+ fqname = sss_create_internal_fqname(tmpctx, name, ddom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_upn(ncache, true, ddom, fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_ncache_set_upn failed (%d [%s]), ignored\n",
+ ret, sss_strerror(ret));
+ }
+ ret = sss_ncache_set_user(ncache, true, ddom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent user filter for [%s]"
+ " (%d [%s])\n", filter_list[i],
+ ret, sss_strerror(ret));
+ continue;
+ }
+ }
+ }
+ }
+
+ talloc_zfree(filter_list);
+ /* Populate non domain-specific negative cache user entries */
+ ret = confdb_get_string_as_list(cdb, tmpctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_FILTER_USERS, &filter_list);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ for (i = 0; (filter_list && filter_list[i]); i++) {
+ ret = sss_parse_name_for_domains(tmpctx, domain_list,
+ NULL, filter_list[i],
+ &domainname, &name);
+ if (ret == EAGAIN) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Can add [%s] only as UPN to negcache because the "
+ "required domain is not known yet\n", filter_list[i]);
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid name in filterUsers list: [%s] (%d)\n",
+ filter_list[i], ret);
+ continue;
+ }
+ if (domainname) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Adding [%s] to UPN negative cache of all domains.\n",
+ filter_list[i]);
+ for (dom = domain_list;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+ ret = sss_ncache_set_upn(ncache, true, dom, filter_list[i]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_ncache_set_upn failed (%d [%s]), ignored\n",
+ ret, sss_strerror(ret));
+ }
+ }
+
+ /* Add name to domain specific cache for known domain names */
+ dom = responder_get_domain(rctx, domainname);
+ if (dom != NULL) {
+ fqname = sss_create_internal_fqname(tmpctx, name, dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_user(ncache, true, dom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent user filter for [%s]"
+ " (%d [%s])\n", filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ } else {
+ for (dom = domain_list;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+ fqname = sss_create_internal_fqname(tmpctx, name, dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_upn(ncache, true, dom, fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent upn filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, filter_list[i],
+ ret, strerror(ret));
+ }
+
+ ret = sss_ncache_set_user(ncache, true, dom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent user filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ }
+ }
+
+ /* Populate domain-specific negative cache group entries */
+ for (dom = domain_list; dom; dom = get_next_domain(dom, 0)) {
+ conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL, dom->name);
+ if (!conf_path) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_zfree(filter_list);
+ ret = confdb_get_string_as_list(cdb, tmpctx, conf_path,
+ CONFDB_NSS_FILTER_GROUPS, &filter_list);
+ if (ret == ENOENT) continue;
+ if (ret != EOK) goto done;
+
+ for (i = 0; (filter_list && filter_list[i]); i++) {
+ ret = sss_parse_name_for_domains(tmpctx, domain_list,
+ NULL, filter_list[i],
+ &domainname, &name);
+ if (ret != EOK) {
+ /* Groups do not have UPNs, so domain names, if present,
+ * must be known */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid name in filterGroups list: [%s] (%d)\n",
+ filter_list[i], ret);
+ continue;
+ }
+
+ /* Check domain and its sub-domains */
+ for (ddom = dom;
+ ddom != NULL && (ddom == dom || ddom->parent != NULL);
+ ddom = get_next_domain(ddom, SSS_GND_ALL_DOMAINS)) {
+ if (domainname && strcmp(domainname, ddom->name)) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Mismatch between domain name (%s) and name "
+ "set in FQN (%s), skipping group %s\n",
+ ddom->name, domainname, name);
+ continue;
+ }
+
+ fqname = sss_create_internal_fqname(tmpctx, name, ddom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_group(ncache, true, ddom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent group filter for [%s]"
+ " (%d [%s])\n", filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ }
+ }
+
+ talloc_zfree(filter_list);
+ /* Populate non domain-specific negative cache group entries */
+ ret = confdb_get_string_as_list(cdb, tmpctx, CONFDB_NSS_CONF_ENTRY,
+ CONFDB_NSS_FILTER_GROUPS, &filter_list);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ for (i = 0; (filter_list && filter_list[i]); i++) {
+ ret = sss_parse_name_for_domains(tmpctx, domain_list,
+ NULL, filter_list[i],
+ &domainname, &name);
+ if (ret != EOK) {
+ /* Groups do not have UPNs, so domain names, if present,
+ * must be known */
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid name in filterGroups list: [%s] (%d)\n",
+ filter_list[i], ret);
+ continue;
+ }
+ if (domainname) {
+ dom = responder_get_domain(rctx, domainname);
+ if (!dom) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Invalid domain name [%s]\n", domainname);
+ continue;
+ }
+
+ fqname = sss_create_internal_fqname(tmpctx, name, dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_group(ncache, true, dom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent group filter for"
+ " [%s] (%d [%s])\n", filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ } else {
+ for (dom = domain_list;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+ fqname = sss_create_internal_fqname(tmpctx, name, dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_group(ncache, true, dom, fqname);
+ talloc_zfree(fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to store permanent group filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, filter_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ }
+ }
+
+ /* SSSD doesn't handle "root", thus it'll be added to the negative cache
+ * nonetheless what's already added there. */
+ default_list = talloc_array(tmpctx, char *, 2);
+ if (default_list == NULL) {
+ ret= ENOMEM;
+ goto done;
+ }
+ default_list[0] = talloc_strdup(tmpctx, "root");
+ if (default_list[0] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ default_list[1] = NULL;
+
+ /* Populate negative cache users and groups entries for the
+ * "default_list" */
+ for (i = 0; (default_list != NULL && default_list[i] != NULL); i++) {
+ for (dom = domain_list;
+ dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) {
+ fqname = sss_create_internal_fqname(tmpctx,
+ default_list[i],
+ dom->name);
+ if (fqname == NULL) {
+ continue;
+ }
+
+ ret = sss_ncache_set_user(ncache, true, dom, fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to store permanent user filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, default_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+
+ ret = sss_ncache_set_group(ncache, true, dom, fqname);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to store permanent group filter for"
+ " [%s:%s] (%d [%s])\n",
+ dom->name, default_list[i],
+ ret, strerror(ret));
+ continue;
+ }
+ }
+ }
+
+ /* Also add "root" uid and gid to the negative cache */
+ ret = sss_ncache_set_uid(ncache, true, NULL, 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to store permanent uid filter for root (0) "
+ "(%d [%s])\n",
+ ret, strerror(ret));
+ }
+
+ ret = sss_ncache_set_gid(ncache, true, NULL, 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to store permanent gid filter for root (0) "
+ "(%d [%s])\n",
+ ret, strerror(ret));
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmpctx);
+ return ret;
+}
+
+/* Reset permanent negcache after checking the domains */
+errno_t sss_ncache_reset_repopulate_permanent(struct resp_ctx *rctx,
+ struct sss_nc_ctx *ncache)
+{
+ int ret;
+
+ ret = sss_ncache_reset_permanent(ncache);
+ if (ret == EOK) {
+ ret = sss_ncache_prepopulate(ncache, rctx->cdb, rctx);
+ }
+
+ return ret;
+}