From d3013d2c474f49159330e1916d931efb7d3cf532 Mon Sep 17 00:00:00 2001
From: Petr Cech <pcech@redhat.com>
Date: Thu, 5 May 2016 11:16:14 -0400
Subject: [PATCH 1/2] RESPONDERS: Negative caching of local users

This patch adds new option 'neg_cache_locals_timeout' into section
of NSS responder. It allows negative caching of local groups and
users. Default value is 0 which means no caching.

Resolves:
https://fedorahosted.org/sssd/ticket/2928
---
 Makefile.am                                 |  4 ++
 src/confdb/confdb.h                         |  1 +
 src/config/SSSDConfig/__init__.py.in        |  1 +
 src/config/etc/sssd.api.conf                |  1 +
 src/man/sssd.conf.5.xml                     | 13 ++++
 src/responder/common/negcache.c             | 43 +++++++++----
 src/responder/common/negcache.h             |  2 +-
 src/responder/common/negcache_files.c       | 97 +++++++++++++++++++++++++++++
 src/responder/common/negcache_files.h       | 32 ++++++++++
 src/responder/common/responder_common.c     | 22 ++++++-
 src/tests/cmocka/common_mock_resp.c         |  2 +-
 src/tests/cmocka/test_negcache.c            |  4 +-
 src/tests/cmocka/test_responder_cache_req.c |  4 +-
 src/tests/cwrap/Makefile.am                 |  1 +
 14 files changed, 208 insertions(+), 19 deletions(-)
 create mode 100644 src/responder/common/negcache_files.c
 create mode 100644 src/responder/common/negcache_files.h

diff --git a/Makefile.am b/Makefile.am
index dc477249be0dab29cef490cf4fb558b5e8b16d3c..2079fe5639dac3d0a4600831dc5d1ac0d8814cdc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -449,6 +449,7 @@ AM_CPPFLAGS = \
 EXTRA_DIST =
 
 SSSD_RESPONDER_OBJ = \
+    src/responder/common/negcache_files.c \
     src/responder/common/negcache.c \
     src/responder/common/responder_cmd.c \
     src/responder/common/responder_common.c \
@@ -574,6 +575,7 @@ dist_noinst_HEADERS = \
     src/responder/nss/nsssrv_services.h \
     src/responder/nss/nsssrv_mmap_cache.h \
     src/responder/pac/pacsrv.h \
+    src/responder/common/negcache_files.c \
     src/responder/common/negcache.h \
     src/responder/sudo/sudosrv_private.h \
     src/responder/autofs/autofs_private.h \
@@ -1790,6 +1792,7 @@ sss_idmap_tests_LDADD = \
 
 responder_socket_access_tests_SOURCES = \
     src/tests/responder_socket_access-tests.c \
+    src/responder/common/negcache_files.c \
     src/responder/common/negcache.c \
     src/responder/common/responder_common.c \
     src/responder/common/responder_packet.c \
@@ -1887,6 +1890,7 @@ TEST_MOCK_RESP_OBJ = \
      src/tests/cmocka/common_mock_resp_dp.c \
      src/responder/common/responder_packet.c \
      src/responder/common/responder_cmd.c \
+     src/responder/common/negcache_files.c \
      src/responder/common/negcache.c \
      src/responder/common/responder_common.c \
      src/responder/common/responder_cache_req.c
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index c478ef0978b2d1ce302a86a3d536f0447c27fefa..6bffa0bdcd69ace42737f0c28b5199fa36927542 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -84,6 +84,7 @@
 #define CONFDB_RESPONDER_GET_DOMAINS_TIMEOUT "get_domains_timeout"
 #define CONFDB_RESPONDER_CLI_IDLE_TIMEOUT "client_idle_timeout"
 #define CONFDB_RESPONDER_CLI_IDLE_DEFAULT_TIMEOUT 60
+#define CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT "local_negative_timeout"
 
 /* NSS */
 #define CONFDB_NSS_CONF_ENTRY "config/nss"
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 1a0893cbc180394d994b9d97fc0fa863da656549..e80ea7f58fa65597a5c48bb660c4ef2641b9e63f 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -67,6 +67,7 @@ option_strings = {
     'enum_cache_timeout' : _('Enumeration cache timeout length (seconds)'),
     'entry_cache_no_wait_timeout' : _('Entry cache background update timeout length (seconds)'),
     'entry_negative_timeout' : _('Negative cache timeout length (seconds)'),
+    'local_negative_timeout' : _('Files negative cache timeout length (seconds)'),
     'filter_users' : _('Users that SSSD should explicitly ignore'),
     'filter_groups' : _('Groups that SSSD should explicitly ignore'),
     'filter_users_in_groups' : _('Should filtered users appear in groups'),
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index a15f2bd05c3046f8d76b13b3d8f28f9001d8fded..f65a0d8c37afe71f9396dedf3d8c3f774b9f5119 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -33,6 +33,7 @@ certificate_verification = str, None, false
 enum_cache_timeout = int, None, false
 entry_cache_nowait_percentage = int, None, false
 entry_negative_timeout = int, None, false
+local_negative_timeout = int, None, false
 filter_users = list, str, false
 filter_groups = list, str, false
 filter_users_in_groups = bool, None, false
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 09db9cd32673c911991b335e986692e3d8d856d0..8b0e6069f52633db9a29d252b1f50728ea8b2745 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -577,6 +577,19 @@
                     </listitem>
                 </varlistentry>
                 <varlistentry>
+                    <term>local_negative_timeout (integer)</term>
+                    <listitem>
+                        <para>
+                            Specifies for how many seconds nss_sss should keep
+                            local users and groups in negative cache before
+                            trying to look the up in the back end again.
+                        </para>
+                        <para>
+                            Default: 0
+                        </para>
+                    </listitem>
+                </varlistentry>
+                <varlistentry>
                     <term>filter_users, filter_groups (string)</term>
                     <listitem>
                         <para>
diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c
index 1488c12a89e35b06bf00b60a2034ca7f9b64580b..be070079b309162c40af6cfbb1ffa7a4e55189fd 100644
--- a/src/responder/common/negcache.c
+++ b/src/responder/common/negcache.c
@@ -21,6 +21,7 @@
 
 #include "util/util.h"
 #include "confdb/confdb.h"
+#include "responder/common/negcache_files.h"
 #include "responder/common/responder.h"
 #include "responder/common/negcache.h"
 #include <fcntl.h>
@@ -40,6 +41,7 @@
 struct sss_nc_ctx {
     struct tdb_context *tdb;
     uint32_t timeout;
+    uint32_t local_timeout;
 };
 
 typedef int (*ncache_set_byname_fn_t)(struct sss_nc_ctx *, bool,
@@ -60,7 +62,7 @@ static int string_to_tdb_data(char *str, TDB_DATA *ret)
 }
 
 int sss_ncache_init(TALLOC_CTX *memctx,  uint32_t timeout,
-                    struct sss_nc_ctx **_ctx)
+                    uint32_t local_timeout, struct sss_nc_ctx **_ctx)
 {
     struct sss_nc_ctx *ctx;
 
@@ -73,6 +75,7 @@ int sss_ncache_init(TALLOC_CTX *memctx,  uint32_t timeout,
     if (!ctx->tdb) return errno;
 
     ctx->timeout = timeout;
+    ctx->local_timeout = local_timeout;
 
     *_ctx = ctx;
     return EOK;
@@ -139,8 +142,8 @@ done:
     return ret;
 }
 
-static int sss_ncache_set_str(struct sss_nc_ctx *ctx,
-                              char *str, bool permanent)
+static int sss_ncache_set_str(struct sss_nc_ctx *ctx, char *str,
+                              bool permanent, bool is_local)
 {
     TDB_DATA key;
     TDB_DATA data;
@@ -154,7 +157,15 @@ static int sss_ncache_set_str(struct sss_nc_ctx *ctx,
     if (permanent) {
         timest = talloc_strdup(ctx, "0");
     } else {
-        timell = (unsigned long long int)time(NULL) + ctx->timeout;
+        if (is_local == true && ctx->local_timeout > 0) {
+            timell = (unsigned long long int)time(NULL) + ctx->local_timeout;
+        } else {
+            if (ctx->timeout > 0) {
+                timell = (unsigned long long int)time(NULL) + ctx->timeout;
+            } else {
+                return EOK;
+            }
+        }
         timest = talloc_asprintf(ctx, "%llu", timell);
     }
     if (!timest) return ENOMEM;
@@ -300,7 +311,7 @@ static int sss_ncache_set_service_int(struct sss_nc_ctx *ctx, bool permanent,
     str = talloc_asprintf(ctx, "%s/%s/%s", NC_SERVICE_PREFIX, domain, name);
     if (!str) return ENOMEM;
 
-    ret = sss_ncache_set_str(ctx, str, permanent);
+    ret = sss_ncache_set_str(ctx, str, permanent, false);
 
     talloc_free(str);
     return ret;
@@ -446,6 +457,7 @@ int sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert)
 static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent,
                                    const char *domain, const char *name)
 {
+    bool is_local;
     char *str;
     int ret;
 
@@ -454,7 +466,8 @@ static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent,
     str = talloc_asprintf(ctx, "%s/%s/%s", NC_USER_PREFIX, domain, name);
     if (!str) return ENOMEM;
 
-    ret = sss_ncache_set_str(ctx, str, permanent);
+    is_local = is_user_local_by_name(name);
+    ret = sss_ncache_set_str(ctx, str, permanent, is_local);
 
     talloc_free(str);
     return ret;
@@ -463,6 +476,7 @@ static int sss_ncache_set_user_int(struct sss_nc_ctx *ctx, bool permanent,
 static int sss_ncache_set_group_int(struct sss_nc_ctx *ctx, bool permanent,
                                     const char *domain, const char *name)
 {
+    bool is_local;
     char *str;
     int ret;
 
@@ -471,7 +485,8 @@ static int sss_ncache_set_group_int(struct sss_nc_ctx *ctx, bool permanent,
     str = talloc_asprintf(ctx, "%s/%s/%s", NC_GROUP_PREFIX, domain, name);
     if (!str) return ENOMEM;
 
-    ret = sss_ncache_set_str(ctx, str, permanent);
+    is_local = is_group_local_by_name(name);
+    ret = sss_ncache_set_str(ctx, str, permanent, is_local);
 
     talloc_free(str);
     return ret;
@@ -488,7 +503,7 @@ static int sss_ncache_set_netgr_int(struct sss_nc_ctx *ctx, bool permanent,
     str = talloc_asprintf(ctx, "%s/%s/%s", NC_NETGROUP_PREFIX, domain, name);
     if (!str) return ENOMEM;
 
-    ret = sss_ncache_set_str(ctx, str, permanent);
+    ret = sss_ncache_set_str(ctx, str, permanent, false);
 
     talloc_free(str);
     return ret;
@@ -535,6 +550,7 @@ int sss_ncache_set_netgr(struct sss_nc_ctx *ctx, bool permanent,
 int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent,
                        struct sss_domain_info *dom, uid_t uid)
 {
+    bool is_local;
     char *str;
     int ret;
 
@@ -546,7 +562,8 @@ int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent,
     }
     if (!str) return ENOMEM;
 
-    ret = sss_ncache_set_str(ctx, str, permanent);
+    is_local = is_user_local_by_uid(uid);
+    ret = sss_ncache_set_str(ctx, str, permanent, is_local);
 
     talloc_free(str);
     return ret;
@@ -555,6 +572,7 @@ int sss_ncache_set_uid(struct sss_nc_ctx *ctx, bool permanent,
 int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent,
                        struct sss_domain_info *dom, gid_t gid)
 {
+    bool is_local;
     char *str;
     int ret;
 
@@ -566,7 +584,8 @@ int sss_ncache_set_gid(struct sss_nc_ctx *ctx, bool permanent,
     }
     if (!str) return ENOMEM;
 
-    ret = sss_ncache_set_str(ctx, str, permanent);
+    is_local = is_group_local_by_gid(gid);
+    ret = sss_ncache_set_str(ctx, str, permanent, is_local);
 
     talloc_free(str);
     return ret;
@@ -580,7 +599,7 @@ int sss_ncache_set_sid(struct sss_nc_ctx *ctx, bool permanent, const char *sid)
     str = talloc_asprintf(ctx, "%s/%s", NC_SID_PREFIX, sid);
     if (!str) return ENOMEM;
 
-    ret = sss_ncache_set_str(ctx, str, permanent);
+    ret = sss_ncache_set_str(ctx, str, permanent, false);
 
     talloc_free(str);
     return ret;
@@ -595,7 +614,7 @@ int sss_ncache_set_cert(struct sss_nc_ctx *ctx, bool permanent,
     str = talloc_asprintf(ctx, "%s/%s", NC_CERT_PREFIX, cert);
     if (!str) return ENOMEM;
 
-    ret = sss_ncache_set_str(ctx, str, permanent);
+    ret = sss_ncache_set_str(ctx, str, permanent, false);
 
     talloc_free(str);
     return ret;
diff --git a/src/responder/common/negcache.h b/src/responder/common/negcache.h
index 572c723cc5202dac26671458920cb85cdc208fd2..377f97c8b3b20ec5b4a284e08d891737e2e25225 100644
--- a/src/responder/common/negcache.h
+++ b/src/responder/common/negcache.h
@@ -26,7 +26,7 @@ struct sss_nc_ctx;
 
 /* init the in memory negative cache */
 int sss_ncache_init(TALLOC_CTX *memctx, uint32_t timeout,
-                    struct sss_nc_ctx **_ctx);
+                    uint32_t local_timeout, struct sss_nc_ctx **_ctx);
 
 uint32_t sss_ncache_get_timeout(struct sss_nc_ctx *ctx);
 
diff --git a/src/responder/common/negcache_files.c b/src/responder/common/negcache_files.c
new file mode 100644
index 0000000000000000000000000000000000000000..aecffacc11749683a6c2c3f63411284f57c8feef
--- /dev/null
+++ b/src/responder/common/negcache_files.c
@@ -0,0 +1,97 @@
+/*
+   SSSD
+
+   NSS Responder
+
+   Copyright (C) Petr Čech <pcech@redhat.com>	2016
+
+   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 <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include "util/util.h"
+#include "responder/common/negcache_files.h"
+
+#define BUFFER_SIZE 16384
+
+bool is_user_local_by_name(const char *name)
+{
+    struct passwd pwd = {0};
+    struct passwd *pwd_result;
+    char buffer[BUFFER_SIZE];
+    bool is_local = false;
+    int ret;
+
+    ret = getpwnam_r(name, &pwd, buffer, BUFFER_SIZE, &pwd_result);
+    if (ret == EOK && pwd_result != NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC, "User %s is a local user\n", name);
+        is_local = true;
+    }
+
+    return is_local;
+}
+
+bool is_user_local_by_uid(uid_t uid)
+{
+    struct passwd pwd = {0};
+    struct passwd *pwd_result;
+    char buffer[BUFFER_SIZE];
+    bool is_local = false;
+    int ret;
+
+    ret = getpwuid_r(uid, &pwd, buffer, BUFFER_SIZE, &pwd_result);
+    if (ret == EOK && pwd_result != NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC, "User with UID %i is a local user\n", uid);
+        is_local = true;
+    }
+
+    return is_local;
+}
+
+bool is_group_local_by_name(const char *name)
+{
+    struct group grp = {0};
+    struct group *grp_result;
+    char buffer[BUFFER_SIZE];
+    bool is_local = false;
+    int ret;
+
+    ret = getgrnam_r(name, &grp, buffer, BUFFER_SIZE, &grp_result);
+    if (ret == EOK && grp_result != NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Group %s is a local group\n", name);
+        is_local = true;
+    }
+
+    return is_local;
+}
+
+bool is_group_local_by_gid(uid_t gid)
+{
+    struct group grp = {0};
+    struct group *grp_result;
+    char buffer[BUFFER_SIZE];
+    bool is_local = false;
+    int ret;
+
+    ret = getgrgid_r(gid, &grp, buffer, BUFFER_SIZE, &grp_result);
+    if (ret == EOK && grp_result != NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Group with GID %i is a local group\n", gid);
+        is_local = true;
+    }
+
+    return is_local;
+}
+
diff --git a/src/responder/common/negcache_files.h b/src/responder/common/negcache_files.h
new file mode 100644
index 0000000000000000000000000000000000000000..bd9c79b8ce574f78506b6ea1eb5ee215a8efbe0a
--- /dev/null
+++ b/src/responder/common/negcache_files.h
@@ -0,0 +1,32 @@
+/*
+   SSSD
+
+   NSS Responder
+
+   Copyright (C) Petr Čech <pcech@redhat.com>	2016
+
+   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 _NEGCACHE_FILES_H_
+#define _NEGCACHE_FILES_H_
+
+bool is_user_local_by_name(const char *name);
+bool is_user_local_by_uid(uid_t uid);
+
+bool is_group_local_by_name(const char *name);
+bool is_group_local_by_gid(uid_t gid);
+
+#endif /* _NEGCACHE_FILES_H_ */
+
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index 883b22a6548c29738f23a5ccf6747969f4e7146c..f0ddde9c0eeaf53f2b589c99337735ab1101b5ab 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -761,6 +761,7 @@ static errno_t responder_init_ncache(TALLOC_CTX *mem_ctx,
                                      struct sss_nc_ctx **ncache)
 {
     uint32_t neg_timeout;
+    uint32_t locals_timeout;
     int tmp_value;
     int ret;
 
@@ -783,8 +784,27 @@ static errno_t responder_init_ncache(TALLOC_CTX *mem_ctx,
     neg_timeout = tmp_value;
     ret = EOK;
 
+    /* local_timeout */
+    ret = confdb_get_int(cdb, CONFDB_NSS_CONF_ENTRY,
+                         CONFDB_RESPONDER_LOCAL_NEG_TIMEOUT,
+                         0, &tmp_value);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              "Fatal failure of setup negative cache timeout.\n");
+        ret = ENOENT;
+        goto done;
+    }
+
+    if (tmp_value < 0) {
+        ret = EINVAL;
+        goto done;
+    }
+
+    locals_timeout = tmp_value;
+    ret = EOK;
+
     /* negative cache init */
-    ret = sss_ncache_init(mem_ctx, neg_timeout, ncache);
+    ret = sss_ncache_init(mem_ctx, neg_timeout, locals_timeout, ncache);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE,
               "Fatal failure of initializing negative cache.\n");
diff --git a/src/tests/cmocka/common_mock_resp.c b/src/tests/cmocka/common_mock_resp.c
index 8bcf18b800d8f64d5ddff9b6524eb209aa88f213..ce73d1b4542946f41003833d81e0f68086fd13e3 100644
--- a/src/tests/cmocka/common_mock_resp.c
+++ b/src/tests/cmocka/common_mock_resp.c
@@ -42,7 +42,7 @@ mock_rctx(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
-    ret = sss_ncache_init(rctx, 10, &rctx->ncache);
+    ret = sss_ncache_init(rctx, 10, 0, &rctx->ncache);
     if (ret != EOK) {
         talloc_free(rctx);
         return NULL;
diff --git a/src/tests/cmocka/test_negcache.c b/src/tests/cmocka/test_negcache.c
index e309ce6456ddaa425568d263964e7a01a653eaf5..a78341c89cda49fc205e8b48b068f2df1a36229e 100644
--- a/src/tests/cmocka/test_negcache.c
+++ b/src/tests/cmocka/test_negcache.c
@@ -104,7 +104,7 @@ static int setup(void **state)
     ts = talloc(NULL, struct test_state);
     assert_non_null(ts);
 
-    ret  = sss_ncache_init(ts, SHORTSPAN, &ts->ctx);
+    ret  = sss_ncache_init(ts, SHORTSPAN, 0, &ts->ctx);
     assert_int_equal(ret, EOK);
     assert_non_null(ts->ctx);
 
@@ -128,7 +128,7 @@ static void test_sss_ncache_init(void **state)
     memctx = talloc_new(NULL);
     assert_non_null(memctx);
 
-    ret = sss_ncache_init(memctx, SHORTSPAN, &ctx );
+    ret = sss_ncache_init(memctx, SHORTSPAN, 0, &ctx );
     assert_int_equal(ret, EOK);
     assert_non_null(ctx);
 
diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c
index 6c13500cef9ec82dde9d97f97f7fb492fe0e3b8f..f30f1eaaddb653a1a5ad325b2838847b1dec1c22 100644
--- a/src/tests/cmocka/test_responder_cache_req.c
+++ b/src/tests/cmocka/test_responder_cache_req.c
@@ -434,7 +434,7 @@ static int test_single_domain_setup(void **state)
                                test_ctx->tctx->dom, NULL);
     assert_non_null(test_ctx->rctx);
 
-    ret = sss_ncache_init(test_ctx, 10, &test_ctx->ncache);
+    ret = sss_ncache_init(test_ctx, 10, 0, &test_ctx->ncache);
     assert_int_equal(ret, EOK);
 
     check_leaks_push(test_ctx);
@@ -480,7 +480,7 @@ static int test_multi_domain_setup(void **state)
                                test_ctx->tctx->dom, NULL);
     assert_non_null(test_ctx->rctx);
 
-    ret = sss_ncache_init(test_ctx, 10, &test_ctx->ncache);
+    ret = sss_ncache_init(test_ctx, 10, 0, &test_ctx->ncache);
     assert_int_equal(ret, EOK);
 
     check_leaks_push(test_ctx);
diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am
index a5afb8c020ee00b680aac0680bcf6f6495dd8222..ba30bab6ecc1afbf451707c2af777480cfe6fd0d 100644
--- a/src/tests/cwrap/Makefile.am
+++ b/src/tests/cwrap/Makefile.am
@@ -106,6 +106,7 @@ usertools_tests_LDADD = \
 
 responder_common_tests_SOURCES =\
     test_responder_common.c \
+    ../../../src/responder/common/negcache_files.c \
     ../../../src/responder/common/negcache.c \
     ../../../src/responder/common/responder_common.c \
     ../../../src/responder/common/responder_packet.c \
-- 
2.5.5

