summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/responder/nss/nsssrv.c8
-rw-r--r--server/responder/nss/nsssrv.h2
-rw-r--r--server/responder/nss/nsssrv_cmd.c259
-rw-r--r--server/responder/nss/nsssrv_nc.c258
-rw-r--r--server/responder/nss/nsssrv_nc.h46
-rw-r--r--server/server.mk3
-rw-r--r--sssd.spec3
7 files changed, 574 insertions, 5 deletions
diff --git a/server/responder/nss/nsssrv.c b/server/responder/nss/nsssrv.c
index a26f5eda6..f12fb6c5c 100644
--- a/server/responder/nss/nsssrv.c
+++ b/server/responder/nss/nsssrv.c
@@ -32,6 +32,7 @@
#include "popt.h"
#include "util/util.h"
#include "responder/nss/nsssrv.h"
+#include "responder/nss/nsssrv_nc.h"
#include "db/sysdb.h"
#include "confdb/confdb.h"
#include "dbus/dbus.h"
@@ -476,8 +477,15 @@ int nss_process_init(TALLOC_CTX *mem_ctx,
return ret;
}
+ ret = nss_ncache_init(nctx, &nctx->ncache);
+ if (ret != EOK) {
+ DEBUG(0, ("fatal error initializing negative cache\n"));
+ return ret;
+ }
+
nctx->expire_time = 120; /* FIXME: read from conf */
nctx->cache_timeout = 600; /* FIXME: read from conf */
+ nctx->neg_timeout = 15; /* FIXME: read from conf */
DEBUG(1, ("NSS Initialization complete\n"));
diff --git a/server/responder/nss/nsssrv.h b/server/responder/nss/nsssrv.h
index 949961a41..4f5750dee 100644
--- a/server/responder/nss/nsssrv.h
+++ b/server/responder/nss/nsssrv.h
@@ -64,6 +64,8 @@ struct nss_ctx {
char *default_domain;
int cache_timeout;
+ int neg_timeout;
+ struct nss_nc_ctx *ncache;
int expire_time;
time_t last_user_enum;
diff --git a/server/responder/nss/nsssrv_cmd.c b/server/responder/nss/nsssrv_cmd.c
index 4117f9771..ef9dd31d5 100644
--- a/server/responder/nss/nsssrv_cmd.c
+++ b/server/responder/nss/nsssrv_cmd.c
@@ -23,6 +23,7 @@
#include "util/btreemap.h"
#include "responder/common/responder_packet.h"
#include "responder/nss/nsssrv.h"
+#include "responder/nss/nsssrv_nc.h"
#include "db/sysdb.h"
#include <time.h>
#include "confdb/confdb.h"
@@ -54,6 +55,9 @@ struct nss_dom_ctx {
struct sss_domain_info *domain;
bool add_domain;
bool check_provider;
+
+ /* cache results */
+ struct ldb_result *res;
};
struct nss_cmd_table {
@@ -267,7 +271,7 @@ done:
}
static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
- const char *err_msg, void *ptr);
+ const char *err_msg, void *ptr);
static void nss_cmd_getpwnam_callback(void *ptr, int status,
struct ldb_result *res)
@@ -280,6 +284,7 @@ static void nss_cmd_getpwnam_callback(void *ptr, int status,
uint8_t *body;
size_t blen;
bool call_provider = false;
+ bool neghit = false;
int ret;
if (status != LDB_SUCCESS) {
@@ -316,12 +321,38 @@ static void nss_cmd_getpwnam_callback(void *ptr, int status,
}
}
+ if (call_provider && res->count == 0) {
+ /* check negative cache before potentially expensive remote call */
+ ret = nss_ncache_check_user(cctx->nctx->ncache,
+ cctx->nctx->neg_timeout,
+ dctx->domain->name, cmdctx->name);
+ switch (ret) {
+ case EEXIST:
+ DEBUG(2, ("Negative cache hit for getpwnam call\n"));
+ res->count = 0;
+ call_provider = false;
+ neghit = true;
+ break;
+ case ENOENT:
+ break;
+ default:
+ DEBUG(4,("Error processing ncache request: %d [%s]\n",
+ ret, strerror(ret)));
+ }
+ ret = EOK;
+ }
+
if (call_provider) {
/* dont loop forever :-) */
dctx->check_provider = false;
timeout = SSS_CLI_SOCKET_TIMEOUT/2;
+ /* keep around current data in case backend is offline */
+ if (res->count) {
+ dctx->res = talloc_steal(dctx, res);
+ }
+
ret = nss_dp_send_acct_req(cctx->nctx, cmdctx,
nss_cmd_getpwnam_dp_callback, dctx,
timeout, dctx->domain->name, NSS_DP_USER,
@@ -343,6 +374,15 @@ static void nss_cmd_getpwnam_callback(void *ptr, int status,
DEBUG(2, ("No results for getpwnam call\n"));
+ /* set negative cache only if not result of cache check */
+ if (!neghit) {
+ ret = nss_ncache_set_user(cctx->nctx->ncache,
+ dctx->domain->name, cmdctx->name);
+ if (ret != EOK) {
+ NSS_CMD_FATAL_ERROR(cctx);
+ }
+ }
+
ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t),
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
@@ -383,7 +423,7 @@ done:
}
static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
- const char *err_msg, void *ptr)
+ const char *err_msg, void *ptr)
{
struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
@@ -395,13 +435,28 @@ static void nss_cmd_getpwnam_dp_callback(uint16_t err_maj, uint32_t err_min,
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
+
+ if (!dctx->res) {
+ /* return 0 results */
+ dctx->res = talloc_zero(dctx, struct ldb_result);
+ if (!dctx->res) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ nss_cmd_getpwnam_callback(dctx, LDB_SUCCESS, dctx->res);
+ return;
}
ret = sysdb_getpwnam(cmdctx, cctx->nctx->sysdb,
dctx->domain, cmdctx->name,
nss_cmd_getpwnam_callback, dctx);
+
+done:
if (ret != EOK) {
- DEBUG(1, ("Failed to make request to our cache!\n"));
+ DEBUG(1, ("Failed to make request to our cache! (%d [%s])\n",
+ ret, strerror(ret)));
ret = nss_cmd_send_error(cmdctx, ret);
if (ret != EOK) {
@@ -481,6 +536,7 @@ static void nss_cmd_getpwuid_callback(void *ptr, int status,
uint8_t *body;
size_t blen;
bool call_provider = false;
+ bool neghit = false;
int ret;
/* one less to go */
@@ -529,6 +585,27 @@ static void nss_cmd_getpwuid_callback(void *ptr, int status,
}
}
+ if (call_provider && res->count == 0) {
+ /* check negative cache before potentially expensive remote call */
+ ret = nss_ncache_check_uid(cctx->nctx->ncache,
+ cctx->nctx->neg_timeout,
+ cmdctx->id);
+ switch (ret) {
+ case EEXIST:
+ DEBUG(2, ("Negative cache hit for getpwuid call\n"));
+ res->count = 0;
+ call_provider = false;
+ neghit = true;
+ break;
+ case ENOENT:
+ break;
+ default:
+ DEBUG(4,("Error processing ncache request: %d [%s]\n",
+ ret, strerror(ret)));
+ }
+ ret = EOK;
+ }
+
if (call_provider) {
/* yet one more call to go */
@@ -538,6 +615,11 @@ static void nss_cmd_getpwuid_callback(void *ptr, int status,
dctx->check_provider = false;
timeout = SSS_CLI_SOCKET_TIMEOUT/2;
+ /* keep around current data in case backend is offline */
+ if (res->count) {
+ dctx->res = talloc_steal(dctx, res);
+ }
+
ret = nss_dp_send_acct_req(cctx->nctx, cmdctx,
nss_cmd_getpwuid_dp_callback, dctx,
timeout, dctx->domain->name, NSS_DP_USER,
@@ -563,6 +645,14 @@ static void nss_cmd_getpwuid_callback(void *ptr, int status,
DEBUG(2, ("No results for getpwuid call\n"));
+ /* set negative cache only if not result of cache check */
+ if (!neghit) {
+ ret = nss_ncache_set_uid(cctx->nctx->ncache, cmdctx->id);
+ if (ret != EOK) {
+ NSS_CMD_FATAL_ERROR(cctx);
+ }
+ }
+
ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t),
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
@@ -620,11 +710,25 @@ static void nss_cmd_getpwuid_dp_callback(uint16_t err_maj, uint32_t err_min,
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
+
+ if (!dctx->res) {
+ /* return 0 results */
+ dctx->res = talloc_zero(dctx, struct ldb_result);
+ if (!dctx->res) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ nss_cmd_getpwnam_callback(dctx, LDB_SUCCESS, dctx->res);
+ return;
}
ret = sysdb_getpwuid(cmdctx, cctx->nctx->sysdb,
dctx->domain, cmdctx->id,
nss_cmd_getpwuid_callback, dctx);
+
+done:
if (ret != EOK) {
DEBUG(1, ("Failed to make request to our cache!\n"));
@@ -1249,6 +1353,7 @@ static void nss_cmd_getgrnam_callback(void *ptr, int status,
uint8_t *body;
size_t blen;
bool call_provider = false;
+ bool neghit = false;
int ret;
if (status != LDB_SUCCESS) {
@@ -1277,12 +1382,38 @@ static void nss_cmd_getgrnam_callback(void *ptr, int status,
}
}
+ if (call_provider && res->count == 0) {
+ /* check negative cache before potentially expensive remote call */
+ ret = nss_ncache_check_group(cctx->nctx->ncache,
+ cctx->nctx->neg_timeout,
+ dctx->domain->name, cmdctx->name);
+ switch (ret) {
+ case EEXIST:
+ DEBUG(2, ("Negative cache hit for getgrnam call\n"));
+ res->count = 0;
+ call_provider = false;
+ neghit = true;
+ break;
+ case ENOENT:
+ break;
+ default:
+ DEBUG(4,("Error processing ncache request: %d [%s]\n",
+ ret, strerror(ret)));
+ }
+ ret = EOK;
+ }
+
if (call_provider) {
/* dont loop forever :-) */
dctx->check_provider = false;
timeout = SSS_CLI_SOCKET_TIMEOUT/2;
+ /* keep around current data in case backend is offline */
+ if (res->count) {
+ dctx->res = talloc_steal(dctx, res);
+ }
+
ret = nss_dp_send_acct_req(cctx->nctx, cmdctx,
nss_cmd_getgrnam_dp_callback, dctx,
timeout, dctx->domain->name, NSS_DP_GROUP,
@@ -1305,6 +1436,15 @@ static void nss_cmd_getgrnam_callback(void *ptr, int status,
DEBUG(2, ("No results for getgrnam call\n"));
+ /* set negative cache only if not result of cache check */
+ if (!neghit) {
+ ret = nss_ncache_set_group(cctx->nctx->ncache,
+ dctx->domain->name, cmdctx->name);
+ if (ret != EOK) {
+ NSS_CMD_FATAL_ERROR(cctx);
+ }
+ }
+
ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t),
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
@@ -1352,11 +1492,25 @@ static void nss_cmd_getgrnam_dp_callback(uint16_t err_maj, uint32_t err_min,
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
+
+ if (!dctx->res) {
+ /* return 0 results */
+ dctx->res = talloc_zero(dctx, struct ldb_result);
+ if (!dctx->res) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ nss_cmd_getgrnam_callback(dctx, LDB_SUCCESS, dctx->res);
+ return;
}
ret = sysdb_getgrnam(cmdctx, cctx->nctx->sysdb,
dctx->domain, cmdctx->name,
nss_cmd_getgrnam_callback, dctx);
+
+done:
if (ret != EOK) {
DEBUG(1, ("Failed to make request to our cache!\n"));
@@ -1437,6 +1591,7 @@ static void nss_cmd_getgrgid_callback(void *ptr, int status,
uint8_t *body;
size_t blen;
bool call_provider = false;
+ bool neghit = false;
int ret;
/* one less to go */
@@ -1476,6 +1631,27 @@ static void nss_cmd_getgrgid_callback(void *ptr, int status,
}
}
+ if (call_provider && res->count == 0) {
+ /* check negative cache before potentially expensive remote call */
+ ret = nss_ncache_check_gid(cctx->nctx->ncache,
+ cctx->nctx->neg_timeout,
+ cmdctx->id);
+ switch (ret) {
+ case EEXIST:
+ DEBUG(2, ("Negative cache hit for getgrgid call\n"));
+ res->count = 0;
+ call_provider = false;
+ neghit = true;
+ break;
+ case ENOENT:
+ break;
+ default:
+ DEBUG(4,("Error processing ncache request: %d [%s]\n",
+ ret, strerror(ret)));
+ }
+ ret = EOK;
+ }
+
if (call_provider) {
/* yet one more call to go */
@@ -1485,6 +1661,11 @@ static void nss_cmd_getgrgid_callback(void *ptr, int status,
dctx->check_provider = false;
timeout = SSS_CLI_SOCKET_TIMEOUT/2;
+ /* keep around current data in case backend is offline */
+ if (res->count) {
+ dctx->res = talloc_steal(dctx, res);
+ }
+
ret = nss_dp_send_acct_req(cctx->nctx, cmdctx,
nss_cmd_getgrgid_dp_callback, dctx,
timeout, dctx->domain->name, NSS_DP_GROUP,
@@ -1510,6 +1691,14 @@ static void nss_cmd_getgrgid_callback(void *ptr, int status,
DEBUG(2, ("No results for getgrgid call\n"));
+ /* set negative cache only if not result of cache check */
+ if (!neghit) {
+ ret = nss_ncache_set_gid(cctx->nctx->ncache, cmdctx->id);
+ if (ret != EOK) {
+ NSS_CMD_FATAL_ERROR(cctx);
+ }
+ }
+
ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t),
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
@@ -1561,11 +1750,25 @@ static void nss_cmd_getgrgid_dp_callback(uint16_t err_maj, uint32_t err_min,
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
+
+ if (!dctx->res) {
+ /* return 0 results */
+ dctx->res = talloc_zero(dctx, struct ldb_result);
+ if (!dctx->res) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ nss_cmd_getgrgid_callback(dctx, LDB_SUCCESS, dctx->res);
+ return;
}
ret = sysdb_getgrgid(cmdctx, cctx->nctx->sysdb,
dctx->domain, cmdctx->id,
nss_cmd_getgrgid_callback, dctx);
+
+done:
if (ret != EOK) {
DEBUG(1, ("Failed to make request to our cache!\n"));
@@ -2082,11 +2285,25 @@ static void nss_cmd_getinitnam_callback(uint16_t err_maj, uint32_t err_min,
"Error: %u, %u, %s\n"
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
+
+ if (!dctx->res) {
+ /* return 0 results */
+ dctx->res = talloc_zero(dctx, struct ldb_result);
+ if (!dctx->res) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ nss_cmd_getinit_callback(dctx, LDB_SUCCESS, dctx->res);
+ return;
}
ret = sysdb_getpwnam(cmdctx, cctx->nctx->sysdb,
dctx->domain, cmdctx->name,
nss_cmd_getinit_callback, dctx);
+
+done:
if (ret != EOK) {
DEBUG(1, ("Failed to make request to our cache!\n"));
@@ -2109,6 +2326,7 @@ static void nss_cmd_getinit_callback(void *ptr, int status,
uint8_t *body;
size_t blen;
bool call_provider = false;
+ bool neghit = false;
int ret;
if (status != LDB_SUCCESS) {
@@ -2136,12 +2354,38 @@ static void nss_cmd_getinit_callback(void *ptr, int status,
}
}
+ if (call_provider && res->count == 0) {
+ /* check negative cache before potentially expensive remote call */
+ ret = nss_ncache_check_user(cctx->nctx->ncache,
+ cctx->nctx->neg_timeout,
+ dctx->domain->name, cmdctx->name);
+ switch (ret) {
+ case EEXIST:
+ DEBUG(2, ("Negative cache hit for initgr call\n"));
+ res->count = 0;
+ call_provider = false;
+ neghit = false;
+ break;
+ case ENOENT:
+ break;
+ default:
+ DEBUG(4,("Error processing ncache request: %d [%s]\n",
+ ret, strerror(ret)));
+ }
+ ret = EOK;
+ }
+
if (call_provider) {
/* dont loop forever :-) */
dctx->check_provider = false;
timeout = SSS_CLI_SOCKET_TIMEOUT/2;
+ /* keep around current data in case backend is offline */
+ if (res->count) {
+ dctx->res = talloc_steal(dctx, res);
+ }
+
ret = nss_dp_send_acct_req(cctx->nctx, cmdctx,
nss_cmd_getinitnam_callback, dctx,
timeout, dctx->domain->name, NSS_DP_USER,
@@ -2164,6 +2408,15 @@ static void nss_cmd_getinit_callback(void *ptr, int status,
DEBUG(2, ("No results for initgroups call\n"));
+ /* set negative cache only if not result of cache check */
+ if (!neghit) {
+ ret = nss_ncache_set_user(cctx->nctx->ncache,
+ dctx->domain->name, cmdctx->name);
+ if (ret != EOK) {
+ NSS_CMD_FATAL_ERROR(cctx);
+ }
+ }
+
ret = sss_packet_new(cctx->creq, 2*sizeof(uint32_t),
sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
diff --git a/server/responder/nss/nsssrv_nc.c b/server/responder/nss/nsssrv_nc.c
new file mode 100644
index 000000000..74f8369b8
--- /dev/null
+++ b/server/responder/nss/nsssrv_nc.c
@@ -0,0 +1,258 @@
+/*
+ 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 "util/util.h"
+#include <fcntl.h>
+#include <time.h>
+#include "tdb.h"
+
+#define NC_USER_PREFIX "NCUSER"
+#define NC_GROUP_PREFIX "NCGROUP"
+#define NC_UID_PREFIX "NCUID"
+#define NC_GID_PREFIX "NCGID"
+
+struct nss_nc_ctx {
+ struct tdb_context *tdb;
+};
+
+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;
+}
+
+int nss_ncache_init(TALLOC_CTX *memctx, struct nss_nc_ctx **_ctx)
+{
+ struct nss_nc_ctx *ctx;
+
+ ctx = talloc_zero(memctx, struct nss_nc_ctx);
+ if (!ctx) return ENOMEM;
+
+ 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 = ctx;
+ return EOK;
+};
+
+static int nss_ncache_check_str(struct nss_nc_ctx *ctx, char *str, int ttl)
+{
+ TDB_DATA key;
+ TDB_DATA data;
+ unsigned long long int timestamp;
+ bool expired = false;
+ char *ep;
+ int ret;
+
+ 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, 0);
+ if (errno != 0 || *ep != '\0') {
+ /* Malformed entry, remove it and return no entry */
+ expired = true;
+ goto done;
+ }
+
+ if (timestamp + ttl > 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;
+ }
+
+ return ret;
+}
+
+static int nss_ncache_set_str(struct nss_nc_ctx *ctx, char *str)
+{
+ TDB_DATA key;
+ TDB_DATA data;
+ char *timest;
+ int ret;
+
+ ret = string_to_tdb_data(str, &key);
+ if (ret != EOK) return ret;
+
+ timest = talloc_asprintf(ctx, "%llu", (unsigned long long int)time(NULL));
+ if (!timest) return ENOMEM;
+
+ ret = string_to_tdb_data(timest, &data);
+ if (ret != EOK) goto done;
+
+ ret = tdb_store(ctx->tdb, key, data, TDB_REPLACE);
+ if (ret != 0) {
+ DEBUG(1, ("Negative cache failed to set entry: [%s]",
+ tdb_errorstr(ctx->tdb)));
+ ret = EFAULT;
+ }
+
+done:
+ talloc_free(timest);
+ return ret;
+}
+
+int nss_ncache_check_user(struct nss_nc_ctx *ctx, int ttl,
+ 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 = nss_ncache_check_str(ctx, str, ttl);
+
+ talloc_free(str);
+ return ret;
+}
+
+int nss_ncache_check_group(struct nss_nc_ctx *ctx, int ttl,
+ 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 = nss_ncache_check_str(ctx, str, ttl);
+
+ talloc_free(str);
+ return ret;
+}
+
+int nss_ncache_check_uid(struct nss_nc_ctx *ctx, int ttl, uid_t uid)
+{
+ char *str;
+ int ret;
+
+ str = talloc_asprintf(ctx, "%s/%u", NC_UID_PREFIX, uid);
+ if (!str) return ENOMEM;
+
+ ret = nss_ncache_check_str(ctx, str, ttl);
+
+ talloc_free(str);
+ return ret;
+}
+
+int nss_ncache_check_gid(struct nss_nc_ctx *ctx, int ttl, gid_t gid)
+{
+ char *str;
+ int ret;
+
+ str = talloc_asprintf(ctx, "%s/%u", NC_GID_PREFIX, gid);
+ if (!str) return ENOMEM;
+
+ ret = nss_ncache_check_str(ctx, str, ttl);
+
+ talloc_free(str);
+ return ret;
+}
+
+int nss_ncache_set_user(struct nss_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 = nss_ncache_set_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+int nss_ncache_set_group(struct nss_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 = nss_ncache_set_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+int nss_ncache_set_uid(struct nss_nc_ctx *ctx, uid_t uid)
+{
+ char *str;
+ int ret;
+
+ str = talloc_asprintf(ctx, "%s/%u", NC_UID_PREFIX, uid);
+ if (!str) return ENOMEM;
+
+ ret = nss_ncache_set_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
+int nss_ncache_set_gid(struct nss_nc_ctx *ctx, gid_t gid)
+{
+ char *str;
+ int ret;
+
+ str = talloc_asprintf(ctx, "%s/%u", NC_GID_PREFIX, gid);
+ if (!str) return ENOMEM;
+
+ ret = nss_ncache_set_str(ctx, str);
+
+ talloc_free(str);
+ return ret;
+}
+
diff --git a/server/responder/nss/nsssrv_nc.h b/server/responder/nss/nsssrv_nc.h
new file mode 100644
index 000000000..acc9170c6
--- /dev/null
+++ b/server/responder/nss/nsssrv_nc.h
@@ -0,0 +1,46 @@
+/*
+ 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/>.
+*/
+
+#ifndef _NSS_NEG_CACHE_H_
+#define _NSS_NEG_CACHE_H_
+
+struct nss_nc_ctx;
+
+/* init the in memory negative cache */
+int nss_ncache_init(TALLOC_CTX *memctx, struct nss_nc_ctx **_ctx);
+
+/* check if the user is expired according to the passed in time to live */
+int nss_ncache_check_user(struct nss_nc_ctx *ctx, int ttl,
+ const char *domain, const char *name);
+int nss_ncache_check_group(struct nss_nc_ctx *ctx, int ttl,
+ const char *domain, const char *name);
+int nss_ncache_check_uid(struct nss_nc_ctx *ctx, int ttl, uid_t uid);
+int nss_ncache_check_gid(struct nss_nc_ctx *ctx, int ttl, gid_t gid);
+
+/* add a new neg-cache entry setting the timestamp to "now" */
+int nss_ncache_set_user(struct nss_nc_ctx *ctx,
+ const char *domain, const char *name);
+int nss_ncache_set_group(struct nss_nc_ctx *ctx,
+ const char *domain, const char *name);
+int nss_ncache_set_uid(struct nss_nc_ctx *ctx, uid_t uid);
+int nss_ncache_set_gid(struct nss_nc_ctx *ctx, gid_t gid);
+
+#endif /* _NSS_NEG_CACHE_H_ */
diff --git a/server/server.mk b/server/server.mk
index ecd60afb6..9785f6365 100644
--- a/server/server.mk
+++ b/server/server.mk
@@ -41,7 +41,8 @@ LDAP_BE_OBJ = \
NSSSRV_OBJ = \
responder/nss/nsssrv.o \
responder/nss/nsssrv_cmd.o \
- responder/nss/nsssrv_dp.o
+ responder/nss/nsssrv_dp.o \
+ responder/nss/nsssrv_nc.o
INFOPIPE_OBJ = \
infopipe/infopipe.o \
diff --git a/sssd.spec b/sssd.spec
index a40eb85ed..a9d08ae96 100644
--- a/sssd.spec
+++ b/sssd.spec
@@ -13,7 +13,8 @@ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
### Dependencies ###
-Requires: libldb >= 0.9.3
+Requires: libldb = 0.9.3
+Requires: libtdb >= 1.1.3
Requires(preun): initscripts chkconfig
Requires(postun): /sbin/service