/*
Authors:
Jakub Hrozek <jhrozek@redhat.com>
Copyright (C) 2013 Red Hat
SSSD tests: NSS responder tests
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 <talloc.h>
#include <tevent.h>
#include <errno.h>
#include <popt.h>
#include "tests/cmocka/common_mock.h"
#include "tests/cmocka/common_mock_resp.h"
#include "responder/common/negcache.h"
#include "responder/nss/nsssrv_private.h"
#include "sss_client/idmap/sss_nss_idmap.h"
#include "util/util_sss_idmap.h"
#include "util/crypto/sss_crypto.h"
#include "util/crypto/nss/nss_util.h"
#include "db/sysdb_private.h" /* new_subdomain() */
struct nss_test_ctx {
int ncache_hits;
};
/* Mock NSS structure */
struct nss_ctx *
{
if (!nctx) {
return NULL;
}
if (err != IDMAP_SUCCESS) {
return NULL;
}
return nctx;
}
/* Mock reading requests from a client. Use values passed from mock
* instead
*/
{
if (wtype == WRAP_CALL_REAL) {
}
if (len == 0) {
}
return;
}
/* Mock returning result to client. Terminate the unit test instead. */
{
}
{
}
{
return sss_mock_type(enum sss_cli_command);
}
{
return EOK;
}
/* Intercept negative cache lookups */
{
int ret;
}
return ret;
}
{
int ret;
}
return ret;
}
{
int ret;
}
return ret;
}
{
int ret;
}
return ret;
}
/* Mock input from the client library */
{
}
{
}
static void mock_fill_user(void)
{
/* One packet for the entry and one for num entries */
}
static void mock_fill_bysid(void)
{
/* One packet for the entry and one for num entries */
}
static void mock_fill_initgr_user(void)
{
}
{
unsigned i;
if (members == 0) return;
/* Member header , one per member */
for (i=0; i<members; i++) {
}
}
{
/* Sequence of null terminated strings (name, passwd, gecos, dir, shell) */
return EOK;
}
{
unsigned i;
if (*nmem > 0) {
for (i = 0; i < *nmem; i++) {
}
}
/* Make sure we exactly matched the end of the packet */
return EOK;
}
{
unsigned i;
rp = 0;
for (i = 0; i < num_gids; i++) {
}
}
struct sss_domain_info *dom,
struct sysdb_attrs *attrs,
{
char *fqname;
return ENOMEM;
}
/* Prime the cache with a valid user */
return ret;
}
struct sss_domain_info *dom,
struct sysdb_attrs *attrs)
{
char *fqname;
return ENOMEM;
}
return ret;
}
struct sss_domain_info *domain,
const char *shortname,
struct ldb_result **_res)
{
char *fqname;
return ENOMEM;
}
return ret;
}
{
}
struct sss_domain_info *dom,
{
char *fqname;
return ENOMEM;
}
NULL, 300, 0);
return ret;
}
{
int i;
for (i = 0; i < nmem; i++) {
}
}
const char *shortname_group,
struct sss_domain_info *group_dom,
const char *shortname_member,
struct sss_domain_info *member_dom,
enum sysdb_member_type type)
{
if (group_fqname == NULL) {
return ENOMEM;
}
member_dom->name);
if (member_fqname == NULL) {
return ENOMEM;
}
SYSDB_MEMBER_USER, false);
return ret;
}
/* ====================== The tests =============================== */
.pw_uid = 123,
.pw_gid = 456,
};
/* Check getting cached and valid user from cache. Account callback will
* not be called and test_nss_getpwnam_check will make sure the user is
* the same as the test entered before starting
*/
{
return EOK;
}
{
&getpwnam_usr, NULL, 0);
mock_input_user_or_group("testuser");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
/* Test that searching for a nonexistant user yields ENOENT.
* Account callback will be called
*/
{
mock_input_user_or_group("testuser_neg");
/* Wait until the test finishes with ENOENT */
/* Test that subsequent search for a nonexistent user yields
* ENOENT and Account callback is not called, on the other hand
* the ncache functions will be called
*/
mock_input_user_or_group("testuser_neg");
/* Wait until the test finishes with ENOENT */
/* Negative cache was hit this time */
}
.pw_uid = 567,
.pw_gid = 890,
};
{
}
{
return EOK;
}
{
mock_input_user_or_group("testuser_search");
"testuser_search", &res);
/* Wait until the test finishes with EOK */
/* test_nss_getpwnam_search_check will check the user attributes */
"testuser_search", &res);
}
/* Test that searching for a user that is expired in the cache goes to the DP
* which updates the record and the NSS responder returns the updated record
*
* The user's shell attribute is updated.
*/
.pw_uid = 10,
.pw_gid = 11,
};
{
}
{
return EOK;
}
{
const char *shell;
/* Mock client input */
mock_input_user_or_group("testuser_update");
/* Mock client command */
/* Call this function when user is updated by the mock DP request */
/* Call this function to check what the responder returned to the client */
/* Mock output buffer */
/* Fire the command */
/* Wait until the test finishes with EOK */
/* Check the user was updated in the cache */
"testuser_update" , &res);
}
/* Check that a FQDN is returned if the domain is FQDN-only and a
* FQDN is requested
*/
.pw_uid = 124,
.pw_gid = 457,
};
{
return EOK;
}
{
&getpwnam_fqdn, NULL, 0);
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
/* Check that a user with a space in his username is returned fine.
*/
.pw_uid = 225,
.pw_gid = 558,
};
{
return EOK;
}
{
/* Prime the cache with a valid user */
&getpwnam_space, NULL, 0);
mock_input_user_or_group("space user");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
return EOK;
}
{
/* Set whitespace substitution */
mock_input_user_or_group("space user");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
/* Set whitespace substitution */
mock_input_user_or_group("space_user");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
/*
* Check that FQDN processing is able to handle arbitrarily sized
* delimeter
*/
.pw_uid = 125,
.pw_gid = 458,
};
{
return EOK;
}
{
/* Prime the cache with a valid user */
&getpwnam_fancy_fqdn, NULL, 0);
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
/* Check getting cached and valid id from cache. Account callback will
* not be called and test_nss_getpwuid_check will make sure the id is
* the same as the test entered before starting
*/
.pw_uid = 101,
.pw_gid = 401,
};
{
return EOK;
}
{
/* Prime the cache with a valid user */
&getpwuid_usr, NULL, 0);
/* Query for that id, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
/* Test that searching for a nonexistent id yields ENOENT.
* Account callback will be called
*/
{
/* Wait until the test finishes with ENOENT */
/* Test that subsequent search for a nonexistent id yields
* ENOENT and Account callback is not called, on the other hand
* the ncache functions will be called
*/
/* Wait until the test finishes with ENOENT */
/* Negative cache was hit this time */
}
/* Test that lookup by UID for a user that does
* not exist in the cache fetches the user from DP
*/
.pw_uid = 107,
.pw_gid = 987,
};
{
}
{
return EOK;
}
{
/* Wait until the test finishes with EOK */
/* test_nss_getpwuid_search_check will check the id attributes */
}
/* Test that searching for an id that is expired in the cache goes to the DP
* which updates the record and the NSS responder returns the updated record
*
* The user's shell attribute is updated.
*/
.pw_uid = 109,
.pw_gid = 11000,
};
{
}
{
return EOK;
}
{
const char *shell;
/* Prime the cache with a valid but expired user */
/* Mock client input */
/* Mock client command */
/* Call this function when id is updated by the mock DP request */
/* Call this function to check what the responder returned to the client */
/* Mock output buffer */
/* Fire the command */
/* Wait until the test finishes with EOK */
/* Check the user was updated in the cache */
}
/* Testsuite setup and teardown */
void **state)
{
/* FIXME - perhaps this should be folded into sssd_domain_init or stricty
* used together
*/
/* Initialize the NSS responder */
/* Create client context */
/* Add nss specific state_ctx */
/* do after previous setup as the former nulls procotol_ctx */
}
.gr_gid = 1123,
};
{
int ret;
assert_int_equal(nmem, 0);
return EOK;
}
/* Test that requesting a valid, cached group with no members returns a valid
* group structure
*/
{
/* Prime the cache with a valid group */
&getgrnam_no_members, 0);
/* Query for that group, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
.pw_uid = 2001,
.pw_gid = 456,
};
.pw_uid = 2002,
.pw_gid = 456,
};
.gr_gid = 1124,
};
{
int ret;
};
return EOK;
}
/* Test that requesting a valid, cached group with some members returns a valid
* group structure with those members present
*/
{
&testgroup_members, 0);
&testmember1, NULL, 0);
&testmember2, NULL, 0);
mock_input_user_or_group("testgroup_members");
/* Query for that group, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
int ret;
};
return EOK;
}
/* Test that requesting a valid, cached group with some members returns a valid
* group structure with those members present as fully qualified names
*/
{
/* Query for that group, call a callback when command finishes */
/* Wait until the test finishes with EOK */
/* Restore FQDN settings */
}
/* Test that requesting a valid, cached group with subdomain members returns
* a valid * group structure with those members present as fully
* qualified names
*/
.pw_uid = 4001,
.pw_gid = 456,
};
.pw_uid = 4002,
.pw_gid = 456,
};
.gr_gid = 2002,
};
{
int ret;
};
return EOK;
}
{
&testsubdomgroup, 0);
&submember1, NULL, 0);
&submember2, NULL, 0);
/* Query for that group, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
int ret;
};
return EOK;
}
{
mock_input_user_or_group("testgroup_members");
/* Query for that group, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
int ret;
};
return EOK;
}
{
/* Query for that group, call a callback when command finishes */
/* Wait until the test finishes with EOK */
/* Restore FQDN settings */
}
{
int ret;
};
/* Important: this member is from a non-qualified domain, so his name will
* not be qualified either
*/
return EOK;
}
{
/* Query for that group, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
.gr_gid = 2123,
};
{
int ret;
assert_int_equal(nmem, 0);
return EOK;
}
/* Test that requesting a valid, cached group with space in its name returns a valid
* group structure
*/
{
/* Prime the cache with a valid group */
&space_group, 0);
mock_input_user_or_group("space group");
/* Query for that group, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
int ret;
assert_int_equal(nmem, 0);
return EOK;
}
/* Test that requesting a valid, cached group with space in its name returns a valid
* group structure
*/
{
/* Set whitespace substitution */
mock_input_user_or_group("space group");
/* Query for that group, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
const char *name;
if (expected_result == NULL) {
assert_int_equal(blen, 0);
} else {
}
return EOK;
}
{
/* Wait until the test finishes with EOK */
}
{
/* Wait until the test finishes with EOK */
}
{
/* Wait until the test finishes with EOK */
}
{
/* Wait until the test finishes with EOK */
}
{
"BUILTIN\\Cryptographic Operators", NULL};
size_t c;
/* Wait until the test finishes with EOK */
}
}
{
size_t c;
/* Wait until the test finishes with EOK */
}
}
{
"CREATOR AUTHORITY\\CREATOR OWNER", NULL };
size_t c;
/* Wait until the test finishes with EOK */
}
}
{
const char *s;
/* Sequence of null terminated strings */
assert_string_equal(s, "S-1-2-3-4");
assert_string_equal(s, "orig_name");
assert_string_equal(s, "1234");
assert_string_equal(s, "testuserorig@upndomain.test");
return EOK;
}
.pw_uid = 1234,
.pw_gid = 5678,
};
{
"orig_name");
/* Prime the cache with a valid user */
mock_input_user_or_group("testuserorig");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
/* Also test looking up the same stuff with UPN */
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
const char *s;
/* Sequence of null terminated strings */
assert_string_equal(s, "S-1-2-3-4");
assert_string_equal(s, "orig_name");
assert_string_equal(s, "1234");
assert_string_equal(s, "phone");
assert_string_equal(s, "+12-34 56 78");
assert_string_equal(s, "mobile");
assert_string_equal(s, "+98-76 54 32");
return EOK;
}
.pw_uid = 2345,
.pw_gid = 6789,
};
{
"orig_name");
&orig_extra, attrs, 0);
mock_input_user_or_group("testuserorigextra");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
const char *s;
/* Sequence of null terminated strings */
assert_string_equal(s, "S-1-2-3-4");
assert_string_equal(s, "orig_name");
assert_string_equal(s, "1234");
assert_string_equal(s, "cn=abc");
assert_string_equal(s, "cn=def");
assert_string_equal(s, "cn=123");
return EOK;
}
.pw_uid = 3456,
.pw_gid = 7890,
};
{
"orig_name");
&orig_multi, attrs, 0);
mock_input_user_or_group("testuserorigmulti");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
.pw_uid = 34567,
.pw_gid = 45678,
};
{
return EOK;
}
{
/* Prime the cache with a valid user */
mock_input_user_or_group("upnuser@upndomain.test");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
/* Test that searching for a nonexistant user yields ENOENT.
* Account callback will be called
*/
{
mock_input_user_or_group("nosuchupnuser@upndomain.test");
/* Wait until the test finishes with ENOENT */
/* Test that subsequent search for a nonexistent user yields
* ENOENT and Account callback is not called, on the other hand
* the ncache functions will be called
*/
nss_test_ctx->ncache_hits = 0;
mock_input_user_or_group("nosuchupnuser@upndomain.test");
/* Wait until the test finishes with ENOENT */
/* Negative cache was hit this time */
}
{
return EOK;
}
.pw_uid = 321,
.pw_gid = 654,
};
.gr_gid = 3211,
};
.gr_gid = 3212,
};
{
&testinitgr_usr, attrs, 0);
&testinitgr_gr1, 0);
&testinitgr_gr2, 0);
mock_input_user_or_group("testinitgr");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
/* Test that searching for a nonexistant user yields ENOENT.
* Account callback will be called
*/
{
/* Wait until the test finishes with ENOENT */
/* UPN lookup will first hit negcache with the username */
/* Test that subsequent search for a nonexistent user yields
* ENOENT and Account callback is not called, on the other hand
* the ncache functions will be called
*/
nss_test_ctx->ncache_hits = 0;
/* Wait until the test finishes with ENOENT */
/* Negative cache was hit this time */
}
{
test_initgr_neg_by_name("testinitgr_neg", false);
}
.pw_uid = 421,
.pw_gid = 654,
};
.gr_gid = 4211,
};
.gr_gid = 4212,
};
{
&testinitgr_srch_usr, attrs, 0);
&testinitgr_srch_gr1, 0);
&testinitgr_srch_gr2, 0);
return EOK;
}
{
return EOK;
}
{
mock_input_user_or_group("testinitgr_srch");
"testinitgr_srch", &res);
/* Wait until the test finishes with EOK */
/* test_nss_getpwnam_search_check will check the user attributes */
"testinitgr_srch", &res);
}
.pw_uid = 521,
.pw_gid = 654,
};
.gr_gid = 5211,
};
.gr_gid = 5212,
};
{
attrs);
&testinitgr_update_gr2, 0);
return EOK;
}
{
return EOK;
}
{
&testinitgr_update_usr, attrs, 0);
&testinitgr_update_gr1, 0);
mock_input_user_or_group("testinitgr_update");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
.pw_uid = 521,
.pw_gid = 654,
};
.gr_gid = 5221,
};
.gr_gid = 5222,
};
{
&testinitgr_2attr_gr2, 0);
return EOK;
}
{
return EOK;
}
/*
* SYSDB_INITGR_EXPIRE has default value 0 => initgroups was not finished.
* SYSDB_CACHE_EXPIRE has value from future => getpwnam finished successfully
*
* Test result: DP should be contacted for update.
*/
{
0);
&testinitgr_2attr_usr, attrs, 0);
&testinitgr_2attr_gr1, 0);
mock_input_user_or_group("testinitgr_2attr");
mock_account_recv(0, 0, NULL,
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
mock_input_user_or_group("upninitgr@upndomain.test");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
/* Test that searching for a nonexistant user yields ENOENT.
* Account callback will be called
*/
{
test_initgr_neg_by_name("upninitgr_neg@upndomain.test", true);
}
{
{ "enumerate", "false" },
};
return 0;
}
{
{ "enumerate", "false" },
{ "full_name_format", "%1$s@%2$s" },
};
return 0;
}
{
{ "enumerate", "false" },
};
return 0;
}
{
false, false, NULL, 0);
return 0;
}
{
{ "enumerate", "false" },
{ "full_name_format", "%1$s@@@@@%2$s" },
};
return 0;
}
{
return 0;
}
.pw_uid = 12345,
.pw_gid = 6890,
};
{
const char *name;
return EOK;
}
{
char *user_sid;
/* Query for that user, call a callback when command finishes */
/* Should go straight to back end, without contacting DP */
/* Wait until the test finishes with EOK */
}
/* Test that searching for a nonexistant user yields ENOENT.
* Account callback will be called
*/
{
char *user_sid;
/* Wait until the test finishes with ENOENT */
/* Test that subsequent search for a nonexistent user yields
* ENOENT and Account callback is not called, on the other hand
* the ncache functions will be called
*/
/* Wait until the test finishes with ENOENT */
/* Negative cache was hit this time */
}
.pw_uid = 123456,
.pw_gid = 789,
};
{
const char *name;
return EOK;
}
{
&testbysid_update, NULL, 0);
return EOK;
}
{
const char *shell;
char *user_sid;
/* Prime the cache with a valid but expired user */
/* Mock client input */
/* Mock client command */
/* Call this function when user is updated by the mock DP request */
/* Call this function to check what the responder returned to the client */
/* Mock output buffer */
/* Fire the command */
/* Wait until the test finishes with EOK */
/* Check the user was updated in the cache */
}
.pw_uid = 23456,
.pw_gid = 6890,
};
#define TEST_TOKEN_CERT \
"MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA2MjMx" \
"NjMyMDdaFw0xNzA2MjMxNjMyMDdaMDIxEjAQBgNVBAoMCUlQQS5ERVZFTDEcMBoG" \
"A1UEAwwTaXBhLWRldmVsLmlwYS5kZXZlbDCCASIwDQYJKoZIhvcNAQEBBQADggEP" \
"ADCCAQoCggEBALXUq56VlY+Z0aWLLpFAjFfbElPBXGQsbZb85J3cGyPjaMHC9wS+" \
"wjB6Ve4HmQyPLx8hbINdDmbawMHYQvTScLYfsqLtj0Lqw20sUUmedk+Es5Oh9VHo" \
"nd8MavYx25Du2u+T0iSgNIDikXguiwCmtAj8VC49ebbgITcjJGzMmiiuJkV3o93Y" \
"+N4M8URtd7EmzaYZQmNm//s2owFrCYMxpLiURPj+URZVuB72504/Ix7X0HCbA/AV" \
"MBaAFJOq+KAQmPEnNp8Wok23eGTdE7aDMDsGCCsGAQUFBwEBBC8wLTArBggrBgEF" \
"BQcwAYYfaHR0cDovL2lwYS1jYS5pcGEuZGV2ZWwvY2Evb2NzcDAOBgNVHQ8BAf8E" \
"BAMCBPAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHQGA1UdHwRtMGsw" \
"aaAxoC+GLWh0dHA6Ly9pcGEtY2EuaXBhLmRldmVsL2lwYS9jcmwvTWFzdGVyQ1JM" \
"LmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRl" \
"IEF1dGhvcml0eTAdBgNVHQ4EFgQUFaDNd5a53QGpaw5m63hnwXicMQ8wDQYJKoZI" \
"0aQtVmY81xamlXE12ZFpwDX43d+EufBkwCUKFX/+8JFDd2doAyeJxv1xM22kKRpc" \
"AqITPgMsa9ToGMWxjbVpc/X/5YfZixWPF0/eZUTotBj9oaR039UrhGfyN7OguF/G" \
"zotpoBIZmdH+ipYsu58HohHVlM9Wi5H4QmiiXl+Soldkq7eXYlafcmT7wv8+cKwz" \
"Nz0Tm3+eYpFqRo3skr6QzXi525Jkg3r6r+kkhxU="
{
const char *name;
return EOK;
}
{
/* Prime the cache with a valid user */
&testbycert, attrs, 0);
/* Query for that user, call a callback when command finishes */
/* Should go straight to back end, without contacting DP */
/* Wait until the test finishes with EOK */
}
{
/* Wait until the test finishes with ENOENT */
/* Test that subsequent search for a nonexistent user yields
* ENOENT and Account callback is not called, on the other hand
* the ncache functions will be called
*/
/* Wait until the test finishes with ENOENT */
/* Negative cache was hit this time */
}
.pw_uid = 1234,
.pw_gid = 5678,
};
{
const char *name;
if (expected_result == NULL) {
assert_int_equal(blen, 0);
} else {
}
return EOK;
}
{
mock_input_user_or_group("testusersid");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with EOK */
}
{
mock_input_user_or_group("testnosuchsid");
/* Query for that user, call a callback when command finishes */
/* Wait until the test finishes with ENOENT (because there is no such SID */
}
{
int rv;
int no_cleanup = 0;
int opt;
_("Do not delete the test database after a test run"), NULL },
};
};
/* Set debug level to invalid value so we can deside if -d 0 was used. */
switch(opt) {
default:
return 1;
}
}
/* Even though normally the tests should clean up after themselves
* they might not after a failed run. Remove the old db to be sure */
if (rv == 0 && !no_cleanup) {
}
#ifdef HAVE_NSS
/* Cleanup NSS and NSPR to make valgrind happy. */
#endif
return rv;
}