summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2009-03-12 22:59:31 -0400
committerSimo Sorce <ssorce@redhat.com>2009-03-13 13:47:38 -0400
commitdd4d867835a1443cb5a10b7a678d1d12400690d6 (patch)
treea78b211c0ae56369ba22a8dc7add0de3f13c7a77 /server
parent063ef0f15dfc32d15d2e9bdcba819906aa99b948 (diff)
downloadsssd-dd4d867835a1443cb5a10b7a678d1d12400690d6.tar.gz
sssd-dd4d867835a1443cb5a10b7a678d1d12400690d6.tar.bz2
sssd-dd4d867835a1443cb5a10b7a678d1d12400690d6.zip
Implement Negative cache for NSS
As for positive caches, negative caches are implement for all queries except enumerations. Also set the correct requires in sssd.spec as we now depend directly on tdb as well.
Diffstat (limited to 'server')
-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
6 files changed, 572 insertions, 4 deletions
diff --git a/server/responder/nss/nsssrv.c b/server/responder/nss/nsssrv.c
index a26f5eda..f12fb6c5 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 949961a4..4f5750de 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 4117f977..ef9dd31d 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 00000000..74f8369b
--- /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 00000000..acc9170c
--- /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 ecd60afb..9785f636 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 \