diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/tests/cmocka | |
parent | Initial commit. (diff) | |
download | sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip |
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tests/cmocka')
97 files changed, 62703 insertions, 0 deletions
diff --git a/src/tests/cmocka/GPT.INI b/src/tests/cmocka/GPT.INI new file mode 100644 index 0000000..5bad811 --- /dev/null +++ b/src/tests/cmocka/GPT.INI @@ -0,0 +1,3 @@ +[General] +Version=6 +displayName=新しいグループ ポリシー オブジェクト diff --git a/src/tests/cmocka/common_mock.h b/src/tests/cmocka/common_mock.h new file mode 100644 index 0000000..280d56d --- /dev/null +++ b/src/tests/cmocka/common_mock.h @@ -0,0 +1,62 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: Common utilities for tests that exercise domains + + 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 __COMMON_MOCK_H_ +#define __COMMON_MOCK_H_ + +/* + * from cmocka.c: + * These headers or their equivalents should be included prior to + * including + * this header file. + * + * #include <stdarg.h> + * #include <stddef.h> + * #include <setjmp.h> + * + * This allows test applications to use custom definitions of C standard + * library functions and types. + */ +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "tests/common.h" + +#define sss_mock_type(type) ((type) mock()) +#define sss_mock_ptr_type(type) ((type) (uintptr_t) mock()) + +#define sss_will_return_always(fn, value) will_return_count(fn, (value), -1) + +enum sss_test_wrapper_call { + WRAP_CALL_WRAPPER, + WRAP_CALL_REAL +}; + +void list_tests(FILE *file, const char *pref, + const struct CMUnitTest tests[], size_t test_count); + +int sss_cmocka_run_group_tests(const struct CMUnitTest * const tests, + const size_t num_tests, + const char *single); +#endif /* __COMMON_MOCK_H_ */ diff --git a/src/tests/cmocka/common_mock_be.c b/src/tests/cmocka/common_mock_be.c new file mode 100644 index 0000000..a83f0ae --- /dev/null +++ b/src/tests/cmocka/common_mock_be.c @@ -0,0 +1,39 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests: Fake back end + + 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 "tests/cmocka/common_mock_resp.h" + +struct be_ctx *mock_be_ctx(TALLOC_CTX *mem_ctx, struct sss_test_ctx *tctx) +{ + struct be_ctx *be_ctx; + + be_ctx = talloc_zero(mem_ctx, struct be_ctx); + assert_non_null(be_ctx); + + be_ctx->cdb = tctx->confdb; + be_ctx->ev = tctx->ev; + be_ctx->domain = tctx->dom; + be_ctx->conf_path = tctx->conf_dom_path; + + return be_ctx; +} diff --git a/src/tests/cmocka/common_mock_be.h b/src/tests/cmocka/common_mock_be.h new file mode 100644 index 0000000..3397e02 --- /dev/null +++ b/src/tests/cmocka/common_mock_be.h @@ -0,0 +1,30 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests: Fake back end + + 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 __COMMON_MOCK_BE_H_ +#define __COMMON_MOCK_BE_H_ + +#include "tests/cmocka/common_mock.h" + +struct be_ctx *mock_be_ctx(TALLOC_CTX *mem_ctx, struct sss_test_ctx *tctx); + +#endif /* __COMMON_MOCK_BE_H_ */ diff --git a/src/tests/cmocka/common_mock_krb5.c b/src/tests/cmocka/common_mock_krb5.c new file mode 100644 index 0000000..e253119 --- /dev/null +++ b/src/tests/cmocka/common_mock_krb5.c @@ -0,0 +1,103 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests: Tests keytab utilities + + 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/sss_krb5.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_krb5.h" + +int mock_keytab(krb5_context kctx, + const char *kt_path, + krb5_keytab_entry *kt_keys, + size_t nkeys) +{ + krb5_error_code kerr; + krb5_keytab keytab; + size_t n; + + kerr = krb5_kt_resolve(kctx, kt_path, &keytab); + assert_int_equal(kerr, 0); + + for (n = 0; n < nkeys; n++) { + kerr = krb5_kt_add_entry(kctx, keytab, &kt_keys[n]); + assert_int_equal(kerr, 0); + } + + kerr = krb5_kt_close(kctx, keytab); + assert_int_equal(kerr, 0); + + return EOK; +} + +void mock_krb5_keytab_entry(krb5_keytab_entry *kent, + krb5_principal principal, + krb5_timestamp timestamp, + krb5_kvno vno, + krb5_enctype enctype, + const char *key) +{ + memset(kent, 0, sizeof(krb5_keytab_entry)); + + kent->magic = KV5M_KEYTAB_ENTRY; + kent->principal = principal; + kent->timestamp = timestamp; + kent->vno = vno; + kent->key.magic = KV5M_KEYBLOCK; + kent->key.enctype = enctype; + kent->key.length = strlen(key) - 1; + kent->key.contents = (krb5_octet *) discard_const(key); +} + +int mock_keytab_with_contents(TALLOC_CTX *mem_ctx, + const char *keytab_path, + const char *keytab_princ) +{ + krb5_context kctx; + krb5_principal principal; + krb5_error_code kerr; + size_t nkeys = 2; + krb5_keytab_entry keys[nkeys]; + char *keytab_file_name; + + kerr = krb5_init_context(&kctx); + assert_int_equal(kerr, 0); + + keytab_file_name = talloc_asprintf(mem_ctx, "FILE:%s", keytab_path); + assert_non_null(keytab_file_name); + + kerr = krb5_parse_name(kctx, keytab_princ, &principal); + assert_int_equal(kerr, 0); + + memset(&keys, nkeys, nkeys * sizeof(krb5_keytab_entry)); + + mock_krb5_keytab_entry(&keys[0], principal, 12345, 1, 1, "11"); + mock_krb5_keytab_entry(&keys[1], principal, 12345, 1, 2, "12"); + + kerr = mock_keytab(kctx, keytab_file_name, keys, nkeys); + assert_int_equal(kerr, 0); + + krb5_free_principal(kctx, principal); + krb5_free_context(kctx); + talloc_free(keytab_file_name); + + return 0; +} diff --git a/src/tests/cmocka/common_mock_krb5.h b/src/tests/cmocka/common_mock_krb5.h new file mode 100644 index 0000000..5d7247b --- /dev/null +++ b/src/tests/cmocka/common_mock_krb5.h @@ -0,0 +1,47 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests: Tests keytab utilities + + 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 __COMMON_MOCK_KRB5_H_ +#define __COMMON_MOCK_KRB5_H_ + +#include "util/sss_krb5.h" +#include "tests/cmocka/common_mock.h" + +void mock_krb5_keytab_entry(krb5_keytab_entry *kent, + krb5_principal principal, + krb5_timestamp timestamp, + krb5_kvno vno, + krb5_enctype enctype, + const char *key); + +int mock_keytab(krb5_context kctx, + const char *kt_path, + krb5_keytab_entry *kt_keys, + size_t nkeys); + +/* Dummy keys with user-selected principal */ +int mock_keytab_with_contents(TALLOC_CTX *mem_ctx, + const char *keytab_path, + const char *keytab_princ); + +#endif /* __COMMON_MOCK_KRB5_H_ */ diff --git a/src/tests/cmocka/common_mock_resp.c b/src/tests/cmocka/common_mock_resp.c new file mode 100644 index 0000000..f4fc323 --- /dev/null +++ b/src/tests/cmocka/common_mock_resp.c @@ -0,0 +1,86 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: Common utilities for tests that exercise domains + + 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 "tests/cmocka/common_mock_resp.h" + +/* Mock a responder context */ +struct resp_ctx * +mock_rctx(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sss_domain_info *domains, + void *pvt_ctx) +{ + struct resp_ctx *rctx; + errno_t ret; + + rctx = talloc_zero(mem_ctx, struct resp_ctx); + if (!rctx) return NULL; + + ret = sss_ncache_init(rctx, 10, 0, &rctx->ncache); + if (ret != EOK) { + talloc_free(rctx); + return NULL; + } + + rctx->ev = ev; + rctx->domains = domains; + rctx->pvt_ctx = pvt_ctx; + if (domains != NULL) { + ret = sss_resp_populate_cr_domains(rctx); + if (ret != EOK) { + return NULL; + } + } + return rctx; +} + +/* Mock a client context */ +struct cli_ctx * +mock_cctx(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx) +{ + struct cli_ctx *cctx; + + cctx = talloc_zero(mem_ctx, struct cli_ctx); + if (!cctx) return NULL; + + cctx->rctx = rctx; + cctx->ev = rctx->ev; + return cctx; +} + +struct cli_protocol * +mock_prctx(TALLOC_CTX *mem_ctx) +{ + struct cli_protocol *prctx; + + prctx = talloc_zero(mem_ctx, struct cli_protocol); + if (!prctx) return NULL; + + prctx->creq = talloc_zero(prctx, struct cli_request); + if (prctx->creq == NULL) { + talloc_free(prctx); + return NULL; + } + + return prctx; +} diff --git a/src/tests/cmocka/common_mock_resp.h b/src/tests/cmocka/common_mock_resp.h new file mode 100644 index 0000000..c277682 --- /dev/null +++ b/src/tests/cmocka/common_mock_resp.h @@ -0,0 +1,73 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: Common utilities for tests that exercise domains + + 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 __COMMON_MOCK_RESP_H_ +#define __COMMON_MOCK_RESP_H_ + +#include "util/util.h" +#include "responder/common/responder.h" +#include "tests/cmocka/common_mock.h" + +/* Mock a responder context */ +struct resp_ctx * +mock_rctx(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sss_domain_info *domains, + void *pvt_ctx); + +/* Mock a client context */ +struct cli_ctx * +mock_cctx(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx); + +struct cli_protocol * +mock_prctx(TALLOC_CTX *mem_ctx); + +/* When mocking a module that calls sss_dp_get_account_{send,recv} + * requests, your test, when linked against this module, will call + * the mock functions instead. Then you can simulate results of the + * sss_dp_get_account_recv call by calling mock_account_recv. + * + * The mocked sss_sp_get_account_recv shall return the return values + * given with parameters dp_err, dp_ret and msg and optionally also call + * the acct_cb_t callback, if given with the pvt pointer as user data. + * The callback can for instance populate the cache, thus simulating + * Data Provider lookup. + * + * There is also even simpler wrapper called mock_account_recv_simple + * that just finishes the account request with a success. + */ +typedef int (*acct_cb_t)(void *); +typedef int (*resolver_cb_t)(void *); + +void mock_account_recv(uint16_t dp_err, uint32_t dp_ret, char *msg, + acct_cb_t acct_cb, void *pvt); + +void mock_account_recv_simple(void); + +void mock_resolver_recv(uint16_t dp_err, uint32_t dp_ret, char *msg, + resolver_cb_t acct_cb, void *pvt); + +void mock_resolver_recv_simple(void); + +void mock_parse_inp(const char *name, const char *domname, errno_t ret); + +#endif /* __COMMON_MOCK_RESP_H_ */ diff --git a/src/tests/cmocka/common_mock_resp_dp.c b/src/tests/cmocka/common_mock_resp_dp.c new file mode 100644 index 0000000..493231d --- /dev/null +++ b/src/tests/cmocka/common_mock_resp_dp.c @@ -0,0 +1,256 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: Fake Data Provider requests + + 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 "responder/common/responder.h" +#include "tests/cmocka/common_mock_resp.h" + +/* Mock DP requests that finish immediately and return + * mocked values as per previous set by mock_account_recv + */ +struct tevent_req * +sss_dp_get_account_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + enum sss_dp_acct_type type, + const char *opt_name, + uint32_t opt_id, + const char *extra) +{ + return test_req_succeed_send(mem_ctx, rctx->ev); +} + +errno_t +sss_dp_get_account_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *dp_err, + dbus_uint32_t *dp_ret, + const char **err_msg) +{ + acct_cb_t cb; + + *dp_err = sss_mock_type(dbus_uint16_t); + *dp_ret = sss_mock_type(dbus_uint32_t); + *err_msg = sss_mock_ptr_type(char *); + + cb = sss_mock_ptr_type(acct_cb_t); + if (cb) { + (cb)(sss_mock_ptr_type(void *)); + } + + return test_request_recv(req); +} + +struct tevent_req * +sss_dp_resolver_get_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + uint32_t entry_type, + uint32_t query_type, + const char *query_value) +{ + return test_req_succeed_send(mem_ctx, rctx->ev); +} + +errno_t +sss_dp_resolver_get_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *dp_err, + dbus_uint32_t *dp_ret, + const char **err_msg) +{ + resolver_cb_t cb; + + *dp_err = sss_mock_type(dbus_uint16_t); + *dp_ret = sss_mock_type(dbus_uint32_t); + *err_msg = sss_mock_ptr_type(char *); + + cb = sss_mock_ptr_type(resolver_cb_t); + if (cb) { + (cb)(sss_mock_ptr_type(void *)); + } + + return test_request_recv(req); +} + +void mock_resolver_recv(uint16_t dp_err, uint32_t dp_ret, char *msg, + resolver_cb_t cb, void *pvt) +{ + will_return(sss_dp_resolver_get_recv, dp_err); + will_return(sss_dp_resolver_get_recv, dp_ret); + will_return(sss_dp_resolver_get_recv, msg); + + will_return(sss_dp_resolver_get_recv, cb); + if (cb) { + will_return(sss_dp_resolver_get_recv, pvt); + } +} + +void mock_resolver_recv_simple(void) +{ + return mock_resolver_recv(0, 0, NULL, NULL, NULL); +} + +struct tevent_req * +sss_dp_get_ssh_host_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + const char *name, + const char *alias) +{ + return test_req_succeed_send(mem_ctx, rctx->ev); +} + + +errno_t +sss_dp_get_ssh_host_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *dp_err, + dbus_uint32_t *dp_ret, + char **err_msg) +{ + acct_cb_t cb; + + *dp_err = sss_mock_type(dbus_uint16_t); + *dp_ret = sss_mock_type(dbus_uint32_t); + *err_msg = sss_mock_ptr_type(char *); + + cb = sss_mock_ptr_type(acct_cb_t); + if (cb) { + (cb)(sss_mock_ptr_type(void *)); + } + + return test_request_recv(req); +} + +errno_t +sss_dp_req_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + dbus_uint16_t *dp_err, + dbus_uint32_t *dp_ret, + char **err_msg) +{ + acct_cb_t cb; + + *dp_err = sss_mock_type(dbus_uint16_t); + *dp_ret = sss_mock_type(dbus_uint32_t); + *err_msg = sss_mock_ptr_type(char *); + + cb = sss_mock_ptr_type(acct_cb_t); + if (cb) { + (cb)(sss_mock_ptr_type(void *)); + } + + return test_request_recv(req); +} + +void mock_account_recv(uint16_t dp_err, uint32_t dp_ret, char *msg, + acct_cb_t acct_cb, void *pvt) +{ + will_return(sss_dp_get_account_recv, dp_err); + will_return(sss_dp_get_account_recv, dp_ret); + will_return(sss_dp_get_account_recv, msg); + + will_return(sss_dp_get_account_recv, acct_cb); + if (acct_cb) { + will_return(sss_dp_get_account_recv, pvt); + } +} + +void mock_account_recv_simple(void) +{ + return mock_account_recv(0, 0, NULL, NULL, NULL); +} + +struct tevent_req * +sss_parse_inp_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + const char *default_domain, + const char *rawinp) +{ + return test_req_succeed_send(mem_ctx, rctx->ev); +} + +errno_t sss_parse_inp_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + char **_name, char **_domname) +{ + *_name = sss_mock_ptr_type(char *); + *_domname = sss_mock_ptr_type(char *); + + return sss_mock_type(errno_t); +} + +void mock_parse_inp(const char *name, const char *domname, errno_t ret) +{ + will_return(sss_parse_inp_recv, name); + will_return(sss_parse_inp_recv, domname); + will_return(sss_parse_inp_recv, ret); +} + +/* Mock subdomain requests */ +struct tevent_req * +sss_dp_get_domains_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + bool force, + const char *hint) +{ + errno_t ret; + ret = sss_resp_populate_cr_domains(rctx); + if (ret != EOK) { + return NULL; + } + + return test_req_succeed_send(mem_ctx, rctx->ev); +} + +errno_t sss_dp_get_domains_recv(struct tevent_req *req) +{ + return test_request_recv(req); +} + +struct tevent_req * +sss_dp_get_account_domain_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *domain, + bool fast_reply, + enum sss_dp_acct_type type, + uint32_t opt_id, + const char *opt_str) +{ + return test_req_succeed_send(mem_ctx, rctx->ev); +} + +errno_t sss_dp_get_account_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + char **_domain) +{ + errno_t ret; + + ret = sss_mock_type(errno_t); + if (ret == EOK) { + *_domain = sss_mock_ptr_type(char *); + } + return ret; +} diff --git a/src/tests/cmocka/common_mock_sdap.c b/src/tests/cmocka/common_mock_sdap.c new file mode 100644 index 0000000..9bbaaf4 --- /dev/null +++ b/src/tests/cmocka/common_mock_sdap.c @@ -0,0 +1,147 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2013 Red Hat + + 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 <talloc.h> + +#include "util/util.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap.h" +#include "tests/cmocka/common_mock.h" + +struct sdap_id_ctx *mock_sdap_id_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_options *sdap_opts) +{ + struct sdap_id_ctx *sdap_id_ctx; + + sdap_id_ctx = talloc_zero(mem_ctx, struct sdap_id_ctx); + assert_non_null(sdap_id_ctx); + + sdap_id_ctx->be = be_ctx; + sdap_id_ctx->opts = sdap_opts; + + return sdap_id_ctx; +} + +struct sdap_options *mock_sdap_options_ldap(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct confdb_ctx *confdb_ctx, + const char *conf_path) +{ + struct sdap_options *opts = NULL; + errno_t ret; + + ret = ldap_get_options(mem_ctx, domain, confdb_ctx, conf_path, NULL, &opts); + if (ret != EOK) { + return NULL; + } + + return opts; +} + +struct sdap_handle *mock_sdap_handle(TALLOC_CTX *mem_ctx) +{ + struct sdap_handle *handle = talloc_zero(mem_ctx, struct sdap_handle); + + /* we will never connect to any LDAP server and any sdap API that + * access sdap_handle should be mocked, thus returning empty structure + * is enough */ + + return handle; +} + +/* + * Mock sdap_async.c + * + * Every function that is placed in sdap_async.c module has to be mocked, + * to avoid any attempt to communicate with remote servers. Therefore no test + * can be compiled with sdap_async.c. If any of these functions is needed, + * their mock equivalent shall be used. + */ + +bool sdap_has_deref_support_ex(struct sdap_handle *sh, + struct sdap_options *opts, + bool ignore_client) +{ + return sss_mock_type(bool); +} + +bool sdap_has_deref_support(struct sdap_handle *sh, + struct sdap_options *opts) +{ + return sss_mock_type(bool); +} + +struct tevent_req *sdap_get_generic_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct sdap_handle *sh, + const char *search_base, + int scope, + const char *filter, + const char **attrs, + struct sdap_attr_map *map, + int map_num_attrs, + int timeout, + bool allow_paging) +{ + return test_req_succeed_send(mem_ctx, ev); +} + +int sdap_get_generic_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *reply_count, + struct sysdb_attrs ***reply) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + *reply_count = sss_mock_type(size_t); + *reply = sss_mock_ptr_type(struct sysdb_attrs **); + + return sss_mock_type(int); +} + +struct tevent_req * sdap_deref_search_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct sdap_handle *sh, + const char *base_dn, + const char *deref_attr, + const char **attrs, + int num_maps, + struct sdap_attr_map_info *maps, + int timeout) +{ + return test_req_succeed_send(mem_ctx, ev); +} + +int sdap_deref_search_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *reply_count, + struct sdap_deref_attrs ***reply) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + *reply_count = sss_mock_type(size_t); + *reply = talloc_steal(mem_ctx, + sss_mock_ptr_type(struct sdap_deref_attrs **)); + + return EOK; +} diff --git a/src/tests/cmocka/common_mock_sdap.h b/src/tests/cmocka/common_mock_sdap.h new file mode 100644 index 0000000..747287d --- /dev/null +++ b/src/tests/cmocka/common_mock_sdap.h @@ -0,0 +1,40 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2013 Red Hat + + 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 COMMON_MOCK_SDAP_H_ +#define COMMON_MOCK_SDAP_H_ + +#include <talloc.h> + +#include "util/util.h" +#include "providers/ldap/sdap.h" + +struct sdap_options *mock_sdap_options_ldap(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + struct confdb_ctx *confdb_ctx, + const char *conf_path); + +struct sdap_id_ctx *mock_sdap_id_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_options *sdap_opts); + +struct sdap_handle *mock_sdap_handle(TALLOC_CTX *mem_ctx); + +#endif /* COMMON_MOCK_SDAP_H_ */ diff --git a/src/tests/cmocka/common_mock_sysdb_objects.c b/src/tests/cmocka/common_mock_sysdb_objects.c new file mode 100644 index 0000000..5dc9e4e --- /dev/null +++ b/src/tests/cmocka/common_mock_sysdb_objects.c @@ -0,0 +1,203 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2013 Red Hat + + 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 "db/sysdb.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_sysdb_objects.h" + +enum sysdb_attr_type { + SYSDB_ATTR_TYPE_BOOL, + SYSDB_ATTR_TYPE_LONG, + SYSDB_ATTR_TYPE_UINT32, + SYSDB_ATTR_TYPE_TIME, + SYSDB_ATTR_TYPE_STRING +}; + +static enum sysdb_attr_type +get_attr_type(const char *attr) +{ + /* Most attributes in sysdb are strings. Since this is only for the purpose + * of unit tests, we can safe ourselves some time and handle all attributes + * that are not listed amongst other types as string instead of invalid + * or unknown. + */ + + static const char *table_bool[] = { + SYSDB_POSIX, + NULL + }; + + static const char *table_long[] = { + NULL + }; + + static const char *table_uint32[] = { + SYSDB_UIDNUM, SYSDB_GIDNUM, + NULL + }; + + static const char *table_time[] = { + SYSDB_CACHE_EXPIRE, + NULL + }; + + static const char **tables[SYSDB_ATTR_TYPE_STRING] = { + table_bool, table_long, table_uint32, table_time + }; + + enum sysdb_attr_type type; + int i; + + for (type = 0; type < SYSDB_ATTR_TYPE_STRING; type++) { + for (i = 0; tables[type][i] != NULL; i++) { + if (strcmp(attr, tables[type][i]) == 0) { + return type; + } + } + } + + /* we didn't find the attribute, consider it as string */ + return SYSDB_ATTR_TYPE_STRING; +} + +static errno_t +fill_attrs(struct sysdb_attrs *attrs, va_list in_ap) +{ + va_list ap; + const char *attr = NULL; + errno_t ret; + + va_copy(ap, in_ap); + while ((attr = va_arg(ap, const char *)) != NULL) { + switch (get_attr_type(attr)) { + case SYSDB_ATTR_TYPE_STRING: + ret = sysdb_attrs_add_string(attrs, attr, va_arg(ap, const char *)); + break; + case SYSDB_ATTR_TYPE_BOOL: + /* _Bool is implicitly promoted to int in variadic functions */ + ret = sysdb_attrs_add_bool(attrs, attr, va_arg(ap, int)); + break; + case SYSDB_ATTR_TYPE_LONG: + ret = sysdb_attrs_add_long(attrs, attr, va_arg(ap, long int)); + break; + case SYSDB_ATTR_TYPE_UINT32: + ret = sysdb_attrs_add_uint32(attrs, attr, va_arg(ap, uint32_t)); + break; + case SYSDB_ATTR_TYPE_TIME: + ret = sysdb_attrs_add_time_t(attrs, attr, va_arg(ap, time_t)); + break; + } + + if (ret != EOK) { + return ret; + } + } + va_end(ap); + + return EOK; +} + +struct sysdb_attrs * +_mock_sysdb_object(TALLOC_CTX *mem_ctx, + const char *base_dn, + const char *name, + ...) +{ + va_list ap; + struct sysdb_attrs *attrs = NULL; + char *orig_dn = NULL; + errno_t ret; + + attrs = sysdb_new_attrs(mem_ctx); + if (attrs == NULL) { + goto fail; + } + + orig_dn = talloc_asprintf(attrs, "cn=%s,%s", name, base_dn); + if (orig_dn == NULL) { + goto fail; + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_DN, orig_dn); + if (ret != EOK) { + goto fail; + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, name); + if (ret != EOK) { + goto fail; + } + + va_start(ap, name); + ret = fill_attrs(attrs, ap); + va_end(ap); + + if (ret != EOK) { + goto fail; + } + + talloc_free(orig_dn); + return attrs; + +fail: + talloc_free(attrs); + return NULL; +} + +struct sysdb_attrs * +mock_sysdb_group_rfc2307bis(TALLOC_CTX *mem_ctx, + const char *base_dn, + gid_t gid, + const char *name, + const char **members) +{ + struct sysdb_attrs *attrs = NULL; + errno_t ret; + int i; + + attrs = mock_sysdb_object(mem_ctx, base_dn, name, + SYSDB_GIDNUM, gid); + if (attrs == NULL) { + return NULL; + } + + if (members != NULL) { + for (i = 0; members[i] != NULL; i++) { + ret = sysdb_attrs_add_string(attrs, SYSDB_MEMBER, members[i]); + if (ret != EOK) { + talloc_zfree(attrs); + return NULL; + } + } + } + + return attrs; +} + +struct sysdb_attrs * +mock_sysdb_user(TALLOC_CTX *mem_ctx, + const char *base_dn, + uid_t uid, + const char *name) +{ + return mock_sysdb_object(mem_ctx, base_dn, name, + SYSDB_UIDNUM, uid); +} diff --git a/src/tests/cmocka/common_mock_sysdb_objects.h b/src/tests/cmocka/common_mock_sysdb_objects.h new file mode 100644 index 0000000..2d00a3f --- /dev/null +++ b/src/tests/cmocka/common_mock_sysdb_objects.h @@ -0,0 +1,51 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2013 Red Hat + + 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 COMMON_MOCK_SYSDB_OBJECTS_H_ +#define COMMON_MOCK_SYSDB_OBJECTS_H_ + +#include <talloc.h> + +#include "util/util.h" +#include "providers/ldap/sdap.h" + +struct sysdb_attrs * +_mock_sysdb_object(TALLOC_CTX *mem_ctx, + const char *base_dn, + const char *name, + ...); + +#define mock_sysdb_object(mem_ctx, base_dn, name, ...) \ + _mock_sysdb_object(mem_ctx, base_dn, name, ##__VA_ARGS__, NULL) + +struct sysdb_attrs * +mock_sysdb_group_rfc2307bis(TALLOC_CTX *mem_ctx, + const char *base_dn, + gid_t gid, + const char *name, + const char **members); + +struct sysdb_attrs * +mock_sysdb_user(TALLOC_CTX *mem_ctx, + const char *base_dn, + uid_t uid, + const char *name); + +#endif /* COMMON_MOCK_SYSDB_OBJECTS_H_ */ diff --git a/src/tests/cmocka/common_utils.c b/src/tests/cmocka/common_utils.c new file mode 100644 index 0000000..cdb6fd0 --- /dev/null +++ b/src/tests/cmocka/common_utils.c @@ -0,0 +1,66 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2022 Red Hat + + SSSD tests: common helpers + + 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 "tests/cmocka/common_mock.h" + +static struct CMUnitTest select_test(const struct CMUnitTest tests[], + size_t test_count, const char *name) +{ + size_t c; + struct CMUnitTest empty = { 0 }; + + for (c = 0; c < test_count; c++) { + if (strcmp(tests[c].name, name) == 0) { + return tests[c]; + } + } + + return empty; +} + +void list_tests(FILE *file, const char *pref, + const struct CMUnitTest tests[], size_t test_count) +{ + size_t c; + for (c = 0; c < test_count; c++) { + fprintf(file, "%s %s\n", pref == NULL ? "" : pref, tests[c].name); + } +} + +int sss_cmocka_run_group_tests(const struct CMUnitTest * const tests, + const size_t num_tests, + const char *single) +{ + struct CMUnitTest single_test[1]; + + if (single != NULL) { + single_test[0] = select_test(tests, num_tests, single); + if (single_test[0].name == NULL) { + fprintf(stderr, "\nTest [%s] not available.\n\n", single); + return ENOENT; + } + + return _cmocka_run_group_tests("single_test", single_test, 1, + NULL, NULL); + } + return _cmocka_run_group_tests("tests", tests, num_tests, NULL, NULL); +} diff --git a/src/tests/cmocka/confdb/test_confdb.c b/src/tests/cmocka/confdb/test_confdb.c new file mode 100644 index 0000000..1551b1c --- /dev/null +++ b/src/tests/cmocka/confdb/test_confdb.c @@ -0,0 +1,306 @@ +/* + Copyright (C) 2020 Red Hat + + 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/>. +*/ +#define _GNU_SOURCE + +#include <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> + +#include <string.h> + +#include "confdb/confdb.h" +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" +#include "tests/cmocka/common_mock_be.h" + + +#include "confdb/confdb.c" + +#define TESTS_PATH "confdb_" BASE_FILE_STEM +#define TEST_CONF_DB "test_confdb.ldb" + +#define TEST_DOMAIN_ENABLED_1 "enabled_1" +#define TEST_DOMAIN_ENABLED_2 "enabled_2" +#define TEST_DOMAIN_ENABLED_3 "enabled_3" + +#define TEST_DOMAIN_DISABLED_1 "disabled_1" +#define TEST_DOMAIN_DISABLED_2 "disabled_2" +#define TEST_DOMAIN_DISABLED_3 "disabled_3" + + +struct test_ctx { + struct confdb_ctx *confdb; +}; + + +static int confdb_test_setup(void **state) +{ + struct test_ctx *test_ctx; + char *conf_db = NULL; + int ret; + const char *val[2]; + val[1] = NULL; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + + conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_DB); + assert_non_null(conf_db); + + ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db); + assert_int_equal(ret, EOK); + + talloc_free(conf_db); + + /* [sssd] */ + val[0] = "2"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/sssd", "config_file_version", val); + assert_int_equal(ret, EOK); + + val[0] = TEST_DOMAIN_ENABLED_1 ", " TEST_DOMAIN_ENABLED_3 ", " TEST_DOMAIN_DISABLED_3; + ret = confdb_add_param(test_ctx->confdb, true, + "config/sssd", "domains", val); + assert_int_equal(ret, EOK); + + /* [domain/enabled_1] */ + val[0] = "proxy"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_ENABLED_1, "id_provider", val); + assert_int_equal(ret, EOK); + + /* [domain/enabled_2] */ + val[0] = "proxy"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_ENABLED_2, "id_provider", val); + assert_int_equal(ret, EOK); + + val[0] = "true"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_ENABLED_2, "enabled", val); + assert_int_equal(ret, EOK); + + /* [domain/enabled_3] */ + val[0] = "proxy"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_ENABLED_3, "id_provider", val); + assert_int_equal(ret, EOK); + + val[0] = "true"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_ENABLED_3, "enabled", val); + assert_int_equal(ret, EOK); + + /* [domain/disabled_1] */ + val[0] = "proxy"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_DISABLED_1, "id_provider", val); + assert_int_equal(ret, EOK); + + /* [domain/disabled_2] */ + val[0] = "proxy"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_DISABLED_2, "id_provider", val); + assert_int_equal(ret, EOK); + + val[0] = "false"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_DISABLED_2, "enabled", val); + assert_int_equal(ret, EOK); + + /* [domain/disabled_3] */ + val[0] = "proxy"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_DISABLED_3, "id_provider", val); + assert_int_equal(ret, EOK); + + val[0] = "false"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/" TEST_DOMAIN_DISABLED_3, "enabled", val); + assert_int_equal(ret, EOK); + + check_leaks_push(test_ctx); + + *state = test_ctx; + return 0; +} + + +static int confdb_test_teardown(void **state) +{ + struct test_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + + +static void test_confdb_get_domain_enabled(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + int ret; + bool enabled; + struct { + const char* domain; + int ret; + bool enabled; + } expected[] = { + { + TEST_DOMAIN_ENABLED_1, + ENOENT, + false + }, + { + TEST_DOMAIN_ENABLED_2, + EOK, + true + }, + { + TEST_DOMAIN_ENABLED_3, + EOK, + true + }, + { + TEST_DOMAIN_DISABLED_1, + ENOENT, + true + }, + { + TEST_DOMAIN_DISABLED_2, + EOK, + false + }, + { + TEST_DOMAIN_DISABLED_3, + EOK, + false + }, + { + "unexistingdomain", + ENOENT, + false + }, + { + NULL, + ENOENT, + false + }, + }; + + for (int index = 0; expected[index].domain; index++) { + ret = confdb_get_domain_enabled(test_ctx->confdb, expected[index].domain, &enabled); + assert_int_equal(ret, expected[index].ret); + ret = confdb_get_domain_enabled(test_ctx->confdb, expected[index].domain, &enabled); + if (ret == EOK) { + if (expected[index].enabled) { + assert_true(enabled); + } else { + assert_false(enabled); + } + } + } +} + + +static void test_confdb_get_enabled_domain_list(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + TALLOC_CTX* tmp_ctx = talloc_new(NULL); + char** result = NULL; + int ret = EOK; + + const char* expected_enabled_domain_list[] = { + TEST_DOMAIN_ENABLED_1, + TEST_DOMAIN_ENABLED_2, + TEST_DOMAIN_ENABLED_3, + NULL + }; + + ret = confdb_get_enabled_domain_list(test_ctx->confdb, tmp_ctx, &result); + assert_int_equal(EOK, ret); + assert_non_null(result); + for (int index = 0; expected_enabled_domain_list[index]; index++) { + assert_true(string_in_list(expected_enabled_domain_list[index], result, false)); + } + for (int index = 0; result[index]; index++) { + assert_true(string_in_list(result[index], discard_const(expected_enabled_domain_list), false)); + } + + TALLOC_FREE(tmp_ctx); +} + + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + int no_cleanup = 0; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_confdb_get_domain_enabled, + confdb_test_setup, + confdb_test_teardown), + cmocka_unit_test_setup_teardown(test_confdb_get_enabled_domain_list, + confdb_test_setup, + confdb_test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, NULL); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, NULL); + } + + return rv; +} diff --git a/src/tests/cmocka/data_provider/mock_dp.c b/src/tests/cmocka/data_provider/mock_dp.c new file mode 100644 index 0000000..387faa9 --- /dev/null +++ b/src/tests/cmocka/data_provider/mock_dp.c @@ -0,0 +1,121 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2016 Red Hat + + 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 <talloc.h> +#include <tevent.h> + +#include "providers/backend.h" +#include "providers/data_provider/dp_private.h" +#include "providers/data_provider/dp.h" +#include "tests/cmocka/common_mock.h" + +static struct dp_method *mock_dp_methods(TALLOC_CTX *mem_ctx) +{ + struct dp_method *methods; + + methods = talloc_zero_array(mem_ctx, struct dp_method, + DP_METHOD_SENTINEL + 1); + assert_non_null(methods); + + return methods; +} + +static struct dp_target **mock_dp_targets(TALLOC_CTX *mem_ctx) +{ + struct dp_target **targets; + enum dp_targets type; + + targets = talloc_zero_array(mem_ctx, struct dp_target *, + DP_TARGET_SENTINEL + 1); + assert_non_null(targets); + + for (type = 0; type != DP_TARGET_SENTINEL; type++) { + targets[type] = talloc_zero(targets, struct dp_target); + assert_non_null(targets[type]); + + targets[type]->name = dp_target_to_string(type); + targets[type]->module_name = "test-module"; + targets[type]->module = NULL; + targets[type]->methods = mock_dp_methods(targets[type]); + targets[type]->initialized = true; + } + + return targets; +} + +struct data_provider *mock_dp(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx) +{ + struct data_provider *provider; + + provider = talloc_zero(mem_ctx, struct data_provider); + assert_non_null(provider); + + provider->ev = be_ctx->ev; + provider->be_ctx = be_ctx; + provider->terminating = false; + provider->requests.index = 0; + provider->requests.num_active = 0; + provider->requests.active = NULL; + provider->targets = mock_dp_targets(provider); + provider->modules = NULL; + + be_ctx->provider = provider; + + return provider; +} + +struct dp_method *mock_dp_get_methods(struct data_provider *provider, + enum dp_targets target) +{ + struct dp_method *methods; + + assert_non_null(provider); + assert_non_null(provider->targets); + assert_non_null(provider->targets[target]); + + methods = provider->targets[target]->methods; + assert_non_null(methods); + + return methods; +} + +struct dp_req_params *mock_dp_req_params(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + enum dp_targets target, + enum dp_methods method) +{ + struct dp_req_params *params; + + params = talloc_zero(mem_ctx, struct dp_req_params); + if (params == NULL) { + return NULL; + } + + params->ev = ev; + params->be_ctx = be_ctx; + params->domain = domain; + params->target = target; + params->method = method; + + return params; +} diff --git a/src/tests/cmocka/data_provider/mock_dp.h b/src/tests/cmocka/data_provider/mock_dp.h new file mode 100644 index 0000000..c583783 --- /dev/null +++ b/src/tests/cmocka/data_provider/mock_dp.h @@ -0,0 +1,42 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2016 Red Hat + + 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 _MOCK_DP_H_ +#define _MOCK_DP_H_ + +#include <talloc.h> + +#include "providers/backend.h" +#include "providers/data_provider/dp_private.h" + +struct data_provider *mock_dp(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx); + +struct dp_method *mock_dp_get_methods(struct data_provider *provider, + enum dp_targets target); + +struct dp_req_params *mock_dp_req_params(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + enum dp_targets target, + enum dp_methods method); + +#endif /* _MOCK_DP_H_ */ diff --git a/src/tests/cmocka/data_provider/test_dp_builtin.c b/src/tests/cmocka/data_provider/test_dp_builtin.c new file mode 100644 index 0000000..e0a727c --- /dev/null +++ b/src/tests/cmocka/data_provider/test_dp_builtin.c @@ -0,0 +1,191 @@ +/* + Copyright (C) 2015 Red Hat + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <security/pam_modules.h> + +#include "providers/backend.h" +#include "providers/data_provider/dp_private.h" +#include "providers/data_provider/dp_builtin.h" +#include "providers/data_provider/dp.h" +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" +#include "tests/cmocka/common_mock_be.h" +#include "tests/cmocka/data_provider/mock_dp.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_dp_request.ldb" +#define TEST_DOM_NAME "dp_request_test" +#define TEST_ID_PROVIDER "ldap" + +struct test_ctx { + struct sss_test_ctx *tctx; + struct be_ctx *be_ctx; + struct dp_req_params *params; +}; + +static int test_setup(void **state) +{ + struct test_ctx *test_ctx; + + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx); + assert_non_null(test_ctx->be_ctx); + + test_ctx->params = mock_dp_req_params(test_ctx, test_ctx->be_ctx->ev, + test_ctx->be_ctx, NULL, + DPT_ID, DPM_ACCOUNT_HANDLER); + assert_non_null(test_ctx->params); + + check_leaks_push(test_ctx); + + *state = test_ctx; + + return 0; +} + +static int test_teardown(void **state) +{ + struct test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct test_ctx); + assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + + assert_true(leak_check_teardown()); + return 0; +} + +static void test_deny_handler(void **state) +{ + errno_t ret; + struct test_ctx *test_ctx; + struct tevent_req *req; + struct pam_data *pd; + struct pam_data *out_pd; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + pd = talloc_zero(test_ctx, struct pam_data); + assert_non_null(pd); + + req = dp_access_deny_handler_send(test_ctx, NULL, pd, test_ctx->params); + assert_non_null(req); + + tevent_loop_wait(test_ctx->tctx->ev); + + ret = dp_access_deny_handler_recv(test_ctx, req, &out_pd); + assert_int_equal(ret, EOK); + assert_ptr_equal(pd, out_pd); + assert_int_equal(pd->pam_status, PAM_PERM_DENIED); + + talloc_free(req); + talloc_free(pd); +} + +static void test_permit_handler(void **state) +{ + errno_t ret; + struct test_ctx *test_ctx; + struct tevent_req *req; + struct pam_data *pd; + struct pam_data *out_pd; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + pd = talloc_zero(test_ctx, struct pam_data); + assert_non_null(pd); + + req = dp_access_permit_handler_send(test_ctx, NULL, pd, test_ctx->params); + assert_non_null(req); + + tevent_loop_wait(test_ctx->tctx->ev); + + ret = dp_access_permit_handler_recv(test_ctx, req, &out_pd); + assert_int_equal(ret, EOK); + assert_ptr_equal(pd, out_pd); + assert_int_equal(pd->pam_status, PAM_SUCCESS); + + talloc_free(req); + talloc_free(pd); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + int no_cleanup = 0; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_permit_handler, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_deny_handler, + test_setup, + test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + + return rv; +} diff --git a/src/tests/cmocka/data_provider/test_dp_request.c b/src/tests/cmocka/data_provider/test_dp_request.c new file mode 100644 index 0000000..a590ae5 --- /dev/null +++ b/src/tests/cmocka/data_provider/test_dp_request.c @@ -0,0 +1,475 @@ +/* + Copyright (C) 2015 Red Hat + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> + +#include "providers/backend.h" +#include "providers/data_provider/dp_private.h" +#include "providers/data_provider/dp.h" +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" +#include "tests/cmocka/common_mock_be.h" +#include "tests/cmocka/data_provider/mock_dp.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_dp_request.ldb" +#define TEST_DOM_NAME "dp_request_test" +#define TEST_ID_PROVIDER "ldap" + +struct test_ctx { + struct sss_test_ctx *tctx; + struct be_ctx *be_ctx; + struct data_provider *provider; + struct dp_method *dp_methods; +}; + +static int test_setup(void **state) +{ + struct test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx); + test_ctx->provider = mock_dp(test_ctx, test_ctx->be_ctx); + test_ctx->dp_methods = mock_dp_get_methods(test_ctx->provider, DPT_ID); + + check_leaks_push(test_ctx); + + *state = test_ctx; + + return 0; +} + +static int test_teardown(void **state) +{ + struct test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct test_ctx); + assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + + assert_true(leak_check_teardown()); + return 0; +} + +static bool is_be_offline_opt = false; + +bool __wrap_be_is_offline(struct be_ctx *ctx) +{ + return is_be_offline_opt; +} + +#define UID 100001 +#define UID2 100002 +#define UID_FAIL 100003 +#define NAME "test_user" +#define NAME2 "test_user2" +#define REQ_NAME "getpwuid" +#define CID 1 +#define SENDER_NAME "sssd.test" + +struct method_data +{ + int foo; +}; + +struct req_data +{ + uid_t uid; +}; + +struct test_state +{ + uid_t uid; + const char *name; +}; + +static void get_name_by_uid_done(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, + void *pvt); + +static struct tevent_req * +get_name_by_uid_send(TALLOC_CTX *mem_ctx, + struct method_data *md, + struct req_data *req_data, + struct dp_req_params *params) +{ + struct tevent_req *req; + struct test_state *state; + struct tevent_timer *tt; + struct timeval tv; + + req = tevent_req_create(mem_ctx, &state, struct test_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n"); + return NULL; + } + + /* Init state of lookup */ + state->uid = req_data->uid; + + /* Mock lookup */ + tv = tevent_timeval_current_ofs(1, 0); + tt = tevent_add_timer(params->ev, req, tv, get_name_by_uid_done, req); + if (tt == NULL) { + return NULL; + } + + return req; +} + +static void get_name_by_uid_done(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, + void *pvt) +{ + struct tevent_req *req; + struct test_state *state; + + req = talloc_get_type(pvt, struct tevent_req); + state = tevent_req_data(req, struct test_state); + + /* Result */ + if (state->uid == UID) { + state->name = NAME; + } else if (state->uid == UID2) { + state->name = NAME2; + } else { + state->name = NULL; + } + tevent_req_done(req); +} + +struct recv_data +{ + const char *name; +}; + +static errno_t +get_name_by_uid_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct recv_data *recv_data) +{ + struct test_state *state; + + state = tevent_req_data(req, struct test_state); + + if (state->name == NULL) { + return ENOENT; + } else { + recv_data->name = talloc_strdup(recv_data, state->name); + } + return EOK; +} + +static void test_get_name_by_uid(void **state) +{ + errno_t ret; + struct test_ctx *test_ctx; + const char *req_name; + struct tevent_req *req; + struct tevent_req *req2; + struct tevent_req *req3; + struct method_data *md; + struct req_data *req_data; + struct req_data *req_data2; + struct req_data *req_data3; + struct recv_data *recv_data; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + md = talloc(test_ctx, struct method_data); + + dp_set_method(test_ctx->dp_methods, + DPM_ACCOUNT_HANDLER, + get_name_by_uid_send, get_name_by_uid_recv, + md, + struct method_data, struct req_data, struct recv_data); + + /* Prepare request data #1 */ + req_data = talloc_zero(test_ctx, struct req_data); + assert_non_null(req_data); + req_data->uid = UID; /* We are looking for user by UID */ + + /* Prepare request data #2 */ + req_data2 = talloc_zero(test_ctx, struct req_data); + assert_non_null(req_data2); + req_data2->uid = UID_FAIL; /* We are looking for user by UID */ + + /* Prepare request data #3 */ + req_data3 = talloc_zero(test_ctx, struct req_data); + assert_non_null(req_data3); + req_data3->uid = UID2; /* We are looking for user by UID */ + + /* Send request #1 */ + req = dp_req_send(test_ctx, test_ctx->provider, NULL, REQ_NAME, CID, + SENDER_NAME, DPT_ID, DPM_ACCOUNT_HANDLER, 0, req_data, + &req_name); + assert_non_null(req); + assert_string_equal(req_name, REQ_NAME" #1"); + talloc_zfree(req_name); + + /* Send request #2 */ + req2 = dp_req_send(test_ctx, test_ctx->provider, NULL, REQ_NAME, CID, + SENDER_NAME, DPT_ID, DPM_ACCOUNT_HANDLER, 0, req_data2, + &req_name); + assert_non_null(req2); + assert_string_equal(req_name, REQ_NAME" #2"); + talloc_zfree(req_name); + + /* Send request #3 */ + req3 = dp_req_send(test_ctx, test_ctx->provider, NULL, REQ_NAME, CID, + SENDER_NAME, DPT_ID, DPM_ACCOUNT_HANDLER, 0, req_data3, + &req_name); + assert_non_null(req3); + assert_string_equal(req_name, REQ_NAME" #3"); + talloc_zfree(req_name); + + tevent_loop_wait(test_ctx->tctx->ev); + + /* Receive lookup results */ + ret = dp_req_recv_ptr(test_ctx, req, struct recv_data, &recv_data); + assert_int_equal(ret, EOK); + assert_string_equal(recv_data->name, NAME); + talloc_free(recv_data); + + ret = dp_req_recv_ptr(test_ctx, req2, struct recv_data, &recv_data); + assert_int_equal(ret, ENOENT); + + ret = dp_req_recv_ptr(test_ctx, req3, struct recv_data, &recv_data); + assert_int_equal(ret, EOK); + assert_string_equal(recv_data->name, NAME2); + talloc_free(recv_data); + + talloc_free(req_data); + talloc_free(req_data2); + talloc_free(req_data3); + talloc_free(req); + talloc_free(req2); + talloc_free(req3); + talloc_free(md); +} + +static void test_type_mismatch(void **state) +{ + errno_t ret; + struct test_ctx *test_ctx; + const char *req_name; + struct tevent_req *req; + struct method_data *md; + struct req_data *req_data; + struct recv_data *recv_data; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + md = talloc(test_ctx, struct method_data); + assert_non_null(md); + + dp_set_method(test_ctx->dp_methods, + DPM_ACCOUNT_HANDLER, + get_name_by_uid_send, get_name_by_uid_recv, + md, + struct method_data, struct req_data, struct recv_data); + + /* Prepare request data #1 */ + req_data = talloc_zero(test_ctx, struct req_data); + assert_non_null(req_data); + req_data->uid = UID; /* We are looking for user by UID */ + + /* Send request #1 */ + req = dp_req_send(test_ctx, test_ctx->provider, NULL, REQ_NAME, CID, + SENDER_NAME, DPT_ID, DPM_ACCOUNT_HANDLER, 0, req_data, &req_name); + assert_non_null(req); + assert_string_equal(req_name, REQ_NAME" #1"); + talloc_zfree(req_name); + + tevent_loop_wait(test_ctx->tctx->ev); + + /* Receive lookup results */ + ret = dp_req_recv_ptr(test_ctx, req, + struct req_data, /* Wrong data type. */ + &recv_data); + assert_int_equal(ret, ERR_INVALID_DATA_TYPE); + + talloc_free(req_data); + talloc_free(req); + talloc_free(md); +} + +static void test_nonexist_dom(void **state) +{ + errno_t ret; + struct test_ctx *test_ctx; + struct tevent_req *req; + struct method_data *md; + struct req_data *req_data; + struct recv_data *recv_data; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + md = talloc(test_ctx, struct method_data); + + dp_set_method(test_ctx->dp_methods, + DPM_ACCOUNT_HANDLER, + get_name_by_uid_send, get_name_by_uid_recv, + md, + struct method_data, struct req_data, struct recv_data); + + /* Prepare request data #1 */ + req_data = talloc_zero(test_ctx, struct req_data); + assert_non_null(req_data); + req_data->uid = UID; /* We are looking for user by UID */ + + /* Send request #1 */ + req = dp_req_send(test_ctx, test_ctx->provider, + "non-existing domain name", + REQ_NAME, CID, SENDER_NAME, + DPT_ID, DPM_ACCOUNT_HANDLER, + 0, + req_data, NULL); + + assert_non_null(req); + + tevent_loop_wait(test_ctx->tctx->ev); + + /* Receive lookup results */ + ret = dp_req_recv_ptr(test_ctx, req, struct recv_data, &recv_data); + assert_int_equal(ret, ERR_DOMAIN_NOT_FOUND); + + talloc_free(req_data); + talloc_free(req); + talloc_free(md); +} + +static void test_fast_reply(void **state) +{ + errno_t ret; + struct test_ctx *test_ctx; + struct tevent_req *req; + struct method_data *md; + struct req_data *req_data; + struct recv_data *recv_data; + bool backup; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + md = talloc(test_ctx, struct method_data); + + dp_set_method(test_ctx->dp_methods, + DPM_ACCOUNT_HANDLER, + get_name_by_uid_send, get_name_by_uid_recv, + md, + struct method_data, struct req_data, struct recv_data); + + /* Prepare request data #1 */ + req_data = talloc_zero(test_ctx, struct req_data); + assert_non_null(req_data); + req_data->uid = UID; /* We are looking for user by UID */ + + backup = is_be_offline_opt; + is_be_offline_opt = true; + + /* Send request #1 */ + req = dp_req_send(test_ctx, test_ctx->provider, NULL, REQ_NAME, + CID, SENDER_NAME, + DPT_ID, DPM_ACCOUNT_HANDLER, + DP_FAST_REPLY, /* FAST REPLY, don't check online! */ + req_data, NULL); + /* Restore */ + is_be_offline_opt = backup; + + assert_non_null(req); + + tevent_loop_wait(test_ctx->tctx->ev); + + /* Receive lookup results */ + ret = dp_req_recv_ptr(test_ctx, req, struct recv_data, &recv_data); + assert_int_equal(ret, ERR_OFFLINE); + talloc_free(req); + talloc_free(md); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + int no_cleanup = 0; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_get_name_by_uid, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_fast_reply, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_type_mismatch, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_nonexist_dom, + test_setup, + test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + + return rv; +} diff --git a/src/tests/cmocka/dummy_child.c b/src/tests/cmocka/dummy_child.c new file mode 100644 index 0000000..407588c --- /dev/null +++ b/src/tests/cmocka/dummy_child.c @@ -0,0 +1,136 @@ +/* + SSSD + + Tests -- a simple test process that echoes input back + + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 <unistd.h> +#include <stdlib.h> +#include <popt.h> + +#include "util/util.h" +#include "util/child_common.h" + +int main(int argc, const char *argv[]) +{ + int opt; + char *opt_logger = NULL; + poptContext pc; + ssize_t len; + ssize_t written; + errno_t ret; + uint8_t buf[IN_BUF_SIZE]; + const char *action = NULL; + int dumpable; + const char *guitar; + const char *drums; + int timestamp_opt; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + SSSD_LOGGER_OPTS + {"dumpable", 0, POPT_ARG_INT, &dumpable, 0, + _("Allow core dumps"), NULL }, + {"guitar", 0, POPT_ARG_STRING, &guitar, 0, _("Who plays guitar"), NULL }, + {"drums", 0, POPT_ARG_STRING, &drums, 0, _("Who plays drums"), NULL }, + POPT_TABLEEND + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + poptFreeContext(pc); + _exit(1); + } + } + poptFreeContext(pc); + + debug_log_file = "test_dummy_child"; + timestamp_opt = debug_timestamps; /* save value for verification */ + DEBUG_INIT(debug_level, opt_logger); + + action = getenv("TEST_CHILD_ACTION"); + if (action) { + if (strcasecmp(action, "check_extra_args") == 0) { + if (!(strcmp(guitar, "george") == 0 \ + && strcmp(drums, "ringo") == 0)) { + DEBUG(SSSDBG_CRIT_FAILURE, "This band sounds weird\n"); + _exit(1); + } + } else if (strcasecmp(action, "check_only_extra_args") == 0) { + if (timestamp_opt == 1) { + DEBUG(SSSDBG_CRIT_FAILURE, + "debug_timestamp was passed when only extra args " + "should have been\n"); + _exit(1); + } + + if (!(strcmp(guitar, "george") == 0 \ + && strcmp(drums, "ringo") == 0)) { + DEBUG(SSSDBG_CRIT_FAILURE, "This band sounds weird\n"); + _exit(1); + } + } else if (strcasecmp(action, "check_only_extra_args_neg") == 0) { + if (timestamp_opt != 1) { + DEBUG(SSSDBG_CRIT_FAILURE, + "debug_timestamp was not passed as expected\n"); + _exit(1); + } + } else if (strcasecmp(action, "echo") == 0) { + errno = 0; + len = sss_atomic_read_s(STDIN_FILENO, buf, IN_BUF_SIZE); + if (len == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, "read failed [%d][%s].\n", ret, strerror(ret)); + _exit(1); + } + close(STDIN_FILENO); + + errno = 0; + written = sss_atomic_write_s(3, buf, len); + if (written == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, "write failed [%d][%s].\n", ret, + strerror(ret)); + _exit(1); + } + close(STDOUT_FILENO); + + if (written != len) { + DEBUG(SSSDBG_CRIT_FAILURE, "Expected to write %zu bytes, wrote %zu\n", + len, written); + _exit(1); + } + } + } + + DEBUG(SSSDBG_TRACE_FUNC, "test_child completed successfully\n"); + _exit(0); +} diff --git a/src/tests/cmocka/sbus/test_sbus_message.c b/src/tests/cmocka/sbus/test_sbus_message.c new file mode 100644 index 0000000..c01e168 --- /dev/null +++ b/src/tests/cmocka/sbus/test_sbus_message.c @@ -0,0 +1,610 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 "config.h" + +#include <talloc.h> +#include <errno.h> +#include <popt.h> + +#include "util/util.h" +#include "sbus/sbus_message.h" +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" + +#define BASE_PATH "/some/path" + +struct test_ctx { + bool msg_removed; +}; + +static void helper_msg_removed(void *state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(state, struct test_ctx); + + test_ctx->msg_removed = true; +} + +static void helper_msg_watch(struct test_ctx *test_ctx, DBusMessage *msg) +{ + DBusFreeFunction free_fn; + dbus_int32_t data_slot = -1; + dbus_bool_t bret; + + assert_non_null(msg); + + bret = dbus_message_allocate_data_slot(&data_slot); + assert_true(bret); + + free_fn = helper_msg_removed; + bret = dbus_message_set_data(msg, data_slot, test_ctx, free_fn); + assert_true(bret); +} + +static int test_setup(void **state) +{ + struct test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + *state = test_ctx; + + check_leaks_push(test_ctx); + + return 0; +} + +int test_teardown(void **state) +{ + struct test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct test_ctx); + + assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + assert_true(leak_check_teardown()); + + return 0; +} + +void test_sbus_message_bound__null(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + DBusMessage *msg; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + + ret = sbus_message_bound(NULL, msg); + assert_int_equal(ret, EINVAL); + + ret = sbus_message_bound(test_ctx, NULL); + assert_int_equal(ret, EINVAL); + + dbus_message_unref(msg); +} + +void test_sbus_message_bound__unref(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + DBusMessage *msg; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + ret = sbus_message_bound(test_ctx, msg); + assert_int_equal(ret, EOK); + + /* no memory leak should be detected in teardown */ + dbus_message_unref(msg); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_message_bound__free(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + TALLOC_CTX *tmp_ctx; + DBusMessage *msg; + errno_t ret; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + ret = sbus_message_bound(tmp_ctx, msg); + assert_int_equal(ret, EOK); + + talloc_free(tmp_ctx); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_message_bound_steal__null(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + DBusMessage *msg; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + ret = sbus_message_bound_steal(NULL, msg); + assert_int_equal(ret, EINVAL); + + ret = sbus_message_bound_steal(test_ctx, NULL); + assert_int_equal(ret, EINVAL); + + dbus_message_unref(msg); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_message_bound_steal__invalid(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + DBusMessage *msg; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + ret = sbus_message_bound_steal(test_ctx, msg); + assert_int_equal(ret, ERR_INTERNAL); + + dbus_message_unref(msg); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_message_bound_steal__free(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + TALLOC_CTX *tmp_ctx; + TALLOC_CTX *tmp_ctx_steal; + DBusMessage *msg; + errno_t ret; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + tmp_ctx_steal = talloc_new(test_ctx); + assert_non_null(tmp_ctx_steal); + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + ret = sbus_message_bound(tmp_ctx, msg); + assert_int_equal(ret, EOK); + + /* this will increase ref counter of message and add new talloc bound */ + ret = sbus_message_bound_steal(tmp_ctx_steal, msg); + assert_int_equal(ret, EOK); + + talloc_free(tmp_ctx); + assert_false(test_ctx->msg_removed); + talloc_free(tmp_ctx_steal); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_method_create_empty__unref(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + DBusMessage *msg; + + msg = sbus_method_create_empty(NULL, "bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + assert_int_equal(dbus_message_get_type(msg), DBUS_MESSAGE_TYPE_METHOD_CALL); + assert_string_equal(dbus_message_get_destination(msg), "bus.test"); + assert_string_equal(dbus_message_get_path(msg), "/"); + assert_string_equal(dbus_message_get_interface(msg), "iface.test"); + assert_string_equal(dbus_message_get_member(msg), "method"); + + dbus_message_unref(msg); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_method_create_empty__free(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + TALLOC_CTX *tmp_ctx; + DBusMessage *msg; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + msg = sbus_method_create_empty(tmp_ctx, "bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + assert_int_equal(dbus_message_get_type(msg), DBUS_MESSAGE_TYPE_METHOD_CALL); + assert_string_equal(dbus_message_get_destination(msg), "bus.test"); + assert_string_equal(dbus_message_get_path(msg), "/"); + assert_string_equal(dbus_message_get_interface(msg), "iface.test"); + assert_string_equal(dbus_message_get_member(msg), "method"); + + talloc_free(tmp_ctx); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_method_create__unref(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + DBusMessage *msg; + dbus_bool_t dbret; + uint32_t in_value = 32; + uint32_t out_value; + + msg = sbus_method_create(NULL, "bus.test", "/", "iface.test", "method", + DBUS_TYPE_UINT32, &in_value); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + assert_int_equal(dbus_message_get_type(msg), DBUS_MESSAGE_TYPE_METHOD_CALL); + assert_string_equal(dbus_message_get_destination(msg), "bus.test"); + assert_string_equal(dbus_message_get_path(msg), "/"); + assert_string_equal(dbus_message_get_interface(msg), "iface.test"); + assert_string_equal(dbus_message_get_member(msg), "method"); + + dbret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_UINT32, &out_value, + DBUS_TYPE_INVALID); + assert_true(dbret); + assert_int_equal(out_value, 32); + + dbus_message_unref(msg); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_method_create__free(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + TALLOC_CTX *tmp_ctx; + DBusMessage *msg; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + msg = sbus_method_create_empty(tmp_ctx, "bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + assert_int_equal(dbus_message_get_type(msg), DBUS_MESSAGE_TYPE_METHOD_CALL); + assert_string_equal(dbus_message_get_destination(msg), "bus.test"); + assert_string_equal(dbus_message_get_path(msg), "/"); + assert_string_equal(dbus_message_get_interface(msg), "iface.test"); + assert_string_equal(dbus_message_get_member(msg), "method"); + + talloc_free(tmp_ctx); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_signal_create_empty__unref(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + DBusMessage *msg; + + msg = sbus_signal_create_empty(NULL, "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + assert_int_equal(dbus_message_get_type(msg), DBUS_MESSAGE_TYPE_SIGNAL); + assert_null(dbus_message_get_destination(msg)); + assert_string_equal(dbus_message_get_path(msg), "/"); + assert_string_equal(dbus_message_get_interface(msg), "iface.test"); + assert_string_equal(dbus_message_get_member(msg), "method"); + + dbus_message_unref(msg); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_signal_create_empty__free(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + TALLOC_CTX *tmp_ctx; + DBusMessage *msg; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + msg = sbus_signal_create_empty(tmp_ctx, "/", "iface.test", "method"); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + assert_int_equal(dbus_message_get_type(msg), DBUS_MESSAGE_TYPE_SIGNAL); + assert_null(dbus_message_get_destination(msg)); + assert_string_equal(dbus_message_get_path(msg), "/"); + assert_string_equal(dbus_message_get_interface(msg), "iface.test"); + assert_string_equal(dbus_message_get_member(msg), "method"); + + talloc_free(tmp_ctx); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_signal_create__unref(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + DBusMessage *msg; + dbus_bool_t dbret; + uint32_t in_value = 32; + uint32_t out_value; + + msg = sbus_signal_create(NULL, "/", "iface.test", "method", + DBUS_TYPE_UINT32, &in_value); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + assert_int_equal(dbus_message_get_type(msg), DBUS_MESSAGE_TYPE_SIGNAL); + assert_null(dbus_message_get_destination(msg)); + assert_string_equal(dbus_message_get_path(msg), "/"); + assert_string_equal(dbus_message_get_interface(msg), "iface.test"); + assert_string_equal(dbus_message_get_member(msg), "method"); + + dbret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_UINT32, &out_value, + DBUS_TYPE_INVALID); + assert_true(dbret); + assert_int_equal(out_value, 32); + + dbus_message_unref(msg); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_signal_create__free(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); + TALLOC_CTX *tmp_ctx; + DBusMessage *msg; + dbus_bool_t dbret; + uint32_t in_value = 32; + uint32_t out_value; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + msg = sbus_signal_create(tmp_ctx, "/", "iface.test", "method", + DBUS_TYPE_UINT32, &in_value); + assert_non_null(msg); + helper_msg_watch(test_ctx, msg); + + assert_int_equal(dbus_message_get_type(msg), DBUS_MESSAGE_TYPE_SIGNAL); + assert_null(dbus_message_get_destination(msg)); + assert_string_equal(dbus_message_get_path(msg), "/"); + assert_string_equal(dbus_message_get_interface(msg), "iface.test"); + assert_string_equal(dbus_message_get_member(msg), "method"); + + dbret = dbus_message_get_args(msg, NULL, + DBUS_TYPE_UINT32, &out_value, + DBUS_TYPE_INVALID); + assert_true(dbret); + assert_int_equal(out_value, 32); + + talloc_free(tmp_ctx); + assert_true(test_ctx->msg_removed); +} + +void test_sbus_reply_parse__ok(void **state) +{ + DBusMessage *msg; + DBusMessage *reply; + dbus_bool_t dbret; + uint32_t in_value1 = 32; + uint32_t in_value2 = 64; + uint32_t out_value1; + uint32_t out_value2; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + dbus_message_set_serial(msg, 1); + + reply = dbus_message_new_method_return(msg); + assert_non_null(reply); + + dbret = dbus_message_append_args(reply, DBUS_TYPE_UINT32, &in_value1, + DBUS_TYPE_UINT32, &in_value2, + DBUS_TYPE_INVALID); + assert_true(dbret); + + ret = sbus_reply_parse(reply, DBUS_TYPE_UINT32, &out_value1, + DBUS_TYPE_UINT32, &out_value2); + assert_int_equal(ret, EOK); + assert_int_equal(out_value1, in_value1); + assert_int_equal(out_value2, in_value2); + + dbus_message_unref(msg); + dbus_message_unref(reply); +} + +void test_sbus_reply_parse__error(void **state) +{ + DBusMessage *msg; + DBusMessage *reply; + uint32_t out_value1; + uint32_t out_value2; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + dbus_message_set_serial(msg, 1); + + reply = dbus_message_new_error(msg, SBUS_ERROR_KILLED, "Test error!"); + assert_non_null(reply); + + ret = sbus_reply_parse(reply, DBUS_TYPE_UINT32, &out_value1, + DBUS_TYPE_UINT32, &out_value2); + assert_int_equal(ret, ERR_SBUS_KILL_CONNECTION); + + dbus_message_unref(msg); + dbus_message_unref(reply); +} + +void test_sbus_reply_parse__wrong_type(void **state) +{ + DBusMessage *msg; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + dbus_message_set_serial(msg, 1); + + ret = sbus_reply_parse(msg); + assert_int_not_equal(ret, EOK); + + dbus_message_unref(msg); +} + +void test_sbus_reply_check__ok(void **state) +{ + DBusMessage *msg; + DBusMessage *reply; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + dbus_message_set_serial(msg, 1); + + reply = dbus_message_new_method_return(msg); + assert_non_null(reply); + + ret = sbus_reply_check(reply); + assert_int_equal(ret, EOK); + + dbus_message_unref(msg); + dbus_message_unref(reply); +} + +void test_sbus_reply_check__error(void **state) +{ + DBusMessage *msg; + DBusMessage *reply; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + dbus_message_set_serial(msg, 1); + + reply = dbus_message_new_error(msg, SBUS_ERROR_KILLED, "Test error!"); + assert_non_null(reply); + + ret = sbus_reply_check(reply); + assert_int_equal(ret, ERR_SBUS_KILL_CONNECTION); + + dbus_message_unref(msg); + dbus_message_unref(reply); +} + +void test_sbus_reply_check__wrong_type(void **state) +{ + DBusMessage *msg; + errno_t ret; + + msg = dbus_message_new_method_call("bus.test", "/", "iface.test", "method"); + assert_non_null(msg); + dbus_message_set_serial(msg, 1); + + ret = sbus_reply_check(msg); + assert_int_not_equal(ret, EOK); + + dbus_message_unref(msg); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sbus_message_bound__null, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_message_bound__unref, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_message_bound__free, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_message_bound_steal__null, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_message_bound_steal__invalid, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_message_bound_steal__free, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_method_create_empty__unref, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_method_create_empty__free, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_method_create__unref, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_method_create__free, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_signal_create_empty__unref, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_signal_create_empty__free, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_signal_create__unref, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_signal_create__free, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_reply_parse__ok, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_reply_parse__error, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_reply_parse__wrong_type, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_reply_check__ok, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_reply_check__error, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sbus_reply_check__wrong_type, + test_setup, test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/sbus/test_sbus_opath.c b/src/tests/cmocka/sbus/test_sbus_opath.c new file mode 100644 index 0000000..9213261 --- /dev/null +++ b/src/tests/cmocka/sbus/test_sbus_opath.c @@ -0,0 +1,313 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 "config.h" + +#include <talloc.h> +#include <errno.h> +#include <popt.h> + +#include "util/util.h" +#include "sbus/sbus_opath.h" +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" + +#define BASE_PATH "/some/path" + +void test_sbus_opath_strip_prefix(void **state) +{ + const char *prefix = "/org/freedesktop/sssd/"; + const char *path = "/org/freedesktop/sssd/infopipe"; + const char *strip; + + strip = sbus_opath_strip_prefix(path, prefix); + assert_non_null(prefix); + assert_string_equal(strip, "infopipe"); + + strip = sbus_opath_strip_prefix("/other/path", prefix); + assert_null(strip); +} + +void test_sbus_opath_escape_unescape(void **state) +{ + char *escaped; + char *raw; + TALLOC_CTX *mem_ctx; + + assert_true(leak_check_setup()); + mem_ctx = talloc_new(NULL); + + escaped = sbus_opath_escape(mem_ctx, "noescape"); + assert_non_null(escaped); + assert_string_equal(escaped, "noescape"); + raw = sbus_opath_unescape(mem_ctx, escaped); + talloc_free(escaped); + assert_non_null(raw); + assert_string_equal(raw, "noescape"); + talloc_free(raw); + + escaped = sbus_opath_escape(mem_ctx, "redhat.com"); + assert_non_null(escaped); + assert_string_equal(escaped, "redhat_2ecom"); /* dot is 0x2E in ASCII */ + raw = sbus_opath_unescape(mem_ctx, escaped); + talloc_free(escaped); + assert_non_null(raw); + assert_string_equal(raw, "redhat.com"); + talloc_free(raw); + + escaped = sbus_opath_escape(mem_ctx, "path_with_underscore"); + assert_non_null(escaped); + /* underscore is 0x5F in ASCII */ + assert_string_equal(escaped, "path_5fwith_5funderscore"); + raw = sbus_opath_unescape(mem_ctx, escaped); + talloc_free(escaped); + assert_non_null(raw); + assert_string_equal(raw, "path_with_underscore"); + talloc_free(raw); + + /* empty string */ + escaped = sbus_opath_escape(mem_ctx, ""); + assert_non_null(escaped); + assert_string_equal(escaped, "_"); + raw = sbus_opath_unescape(mem_ctx, escaped); + talloc_free(escaped); + assert_non_null(raw); + assert_string_equal(raw, ""); + talloc_free(raw); + + /* negative tests */ + escaped = sbus_opath_escape(mem_ctx, NULL); + assert_null(escaped); + raw = sbus_opath_unescape(mem_ctx, "wrongpath_"); + assert_null(raw); + + assert_true(leak_check_teardown()); +} + +void test_sbus_opath_compose(void **state) +{ + char *path; + + /* Doesn't need escaping */ + path = sbus_opath_compose(NULL, BASE_PATH, "domname"); + assert_non_null(path); + assert_string_equal(path, BASE_PATH "/domname"); + talloc_free(path); +} + +void test_sbus_opath_compose_escape(void **state) +{ + char *path; + + /* A dot needs escaping */ + path = sbus_opath_compose(NULL, BASE_PATH, "redhat.com", NULL); + assert_non_null(path); + assert_string_equal(path, BASE_PATH "/redhat_2ecom"); + talloc_free(path); +} + +static void check_opath_components(char **input, + const char **expected) +{ + int i; + + assert_non_null(input); + assert_non_null(expected); + + for (i = 0; input[i] != NULL; i++) { + assert_non_null(input[i]); + assert_non_null(expected[i]); + assert_string_equal(input[i], expected[i]); + } + + assert_null(input[i]); + assert_null(expected[i]); +} + +static void check_opath_components_and_length(char **input, + size_t input_len, + const char **expected, + size_t expected_len) +{ + assert_true(input_len == expected_len); + check_opath_components(input, expected); +} + +void test_sbus_opath_decompose_noprefix(void **state) +{ + const char *path = "/object/path/parts"; + const char *expected[] = {"object", "path", "parts", NULL}; + size_t expected_len = sizeof(expected) / sizeof(char *) - 1; + char **components; + size_t len; + errno_t ret; + + ret = sbus_opath_decompose(NULL, path, NULL, &components, &len); + assert_int_equal(ret, EOK); + check_opath_components_and_length(components, len, expected, expected_len); + talloc_free(components); +} + +void test_sbus_opath_decompose_prefix(void **state) +{ + const char *path = "/object/path/parts"; + const char *expected[] = {"parts", NULL}; + size_t expected_len = sizeof(expected) / sizeof(char *) - 1; + char **components; + size_t len; + errno_t ret; + + ret = sbus_opath_decompose(NULL, path, "/object/path", &components, &len); + assert_int_equal(ret, EOK); + check_opath_components_and_length(components, len, expected, expected_len); + talloc_free(components); +} + +void test_sbus_opath_decompose_prefix_slash(void **state) +{ + const char *path = "/object/path/parts"; + const char *expected[] = {"parts", NULL}; + size_t expected_len = sizeof(expected) / sizeof(char *) - 1; + char **components; + size_t len; + errno_t ret; + + ret = sbus_opath_decompose(NULL, path, "/object/path/", &components, &len); + assert_int_equal(ret, EOK); + check_opath_components_and_length(components, len, expected, expected_len); + talloc_free(components); +} + +void test_sbus_opath_decompose_wrong_prefix(void **state) +{ + const char *path = "/object/path/parts"; + char **components; + size_t len; + errno_t ret; + + ret = sbus_opath_decompose(NULL, path, "/wrong/prefix", &components, &len); + assert_int_equal(ret, ERR_SBUS_INVALID_PATH); +} + +void test_sbus_opath_decompose_escaped(void **state) +{ + const char *path = "/object/redhat_2ecom"; + const char *expected[] = {"object", "redhat.com", NULL}; + size_t expected_len = sizeof(expected) / sizeof(char *) - 1; + char **components; + size_t len; + errno_t ret; + + ret = sbus_opath_decompose(NULL, path, NULL, &components, &len); + assert_int_equal(ret, EOK); + check_opath_components_and_length(components, len, expected, expected_len); + talloc_free(components); +} + +void test_sbus_opath_decompose_expected_correct(void **state) +{ + const char *path = "/object/path/parts"; + const char *expected[] = {"object", "path", "parts", NULL}; + char **components; + errno_t ret; + + ret = sbus_opath_decompose_expected(NULL, path, NULL, 3, &components); + assert_int_equal(ret, EOK); + check_opath_components(components, expected); + talloc_free(components); +} + +void test_sbus_opath_decompose_expected_wrong(void **state) +{ + const char *path = "/object/path/parts"; + char **components; + errno_t ret; + + ret = sbus_opath_decompose_expected(NULL, path, NULL, 2, &components); + assert_int_equal(ret, ERR_SBUS_INVALID_PATH); +} + +void test_sbus_opath_object_name(void **state) +{ + const char *path = BASE_PATH "/redhat_2ecom"; + char *name; + + name = sbus_opath_object_name(NULL, path, BASE_PATH); + assert_non_null(name); + assert_string_equal(name, "redhat.com"); + talloc_free(name); + + name = sbus_opath_object_name(NULL, path, BASE_PATH "/"); + assert_non_null(name); + assert_string_equal(name, "redhat.com"); + talloc_free(name); + + name = sbus_opath_object_name(NULL, BASE_PATH, BASE_PATH); + assert_null(name); + + name = sbus_opath_object_name(NULL, "invalid", BASE_PATH); + assert_null(name); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sbus_opath_strip_prefix), + cmocka_unit_test(test_sbus_opath_escape_unescape), + cmocka_unit_test(test_sbus_opath_compose), + cmocka_unit_test(test_sbus_opath_compose_escape), + cmocka_unit_test(test_sbus_opath_decompose_noprefix), + cmocka_unit_test(test_sbus_opath_decompose_prefix), + cmocka_unit_test(test_sbus_opath_decompose_prefix_slash), + cmocka_unit_test(test_sbus_opath_decompose_wrong_prefix), + cmocka_unit_test(test_sbus_opath_decompose_escaped), + cmocka_unit_test(test_sbus_opath_decompose_expected_correct), + cmocka_unit_test(test_sbus_opath_decompose_expected_wrong), + cmocka_unit_test(test_sbus_opath_object_name) + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/sss_nss_idmap-tests.c b/src/tests/cmocka/sss_nss_idmap-tests.c new file mode 100644 index 0000000..30b24a5 --- /dev/null +++ b/src/tests/cmocka/sss_nss_idmap-tests.c @@ -0,0 +1,192 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2013 Red Hat + + Test for the NSS Responder ID-SID mapping interface + + 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 <errno.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> + + +#include "util/util.h" +#include "util/sss_endian.h" + +#define IPA_389DS_PLUGIN_HELPER_CALLS 1 +#include "sss_client/idmap/sss_nss_idmap.h" +#include "tests/cmocka/common_mock.h" + +#include <nss.h> +#include "sss_client/sss_cli.h" + +struct sss_nss_make_request_test_data { + uint8_t *repbuf; + size_t replen; + int errnop; + enum nss_status nss_status; +}; + +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +uint8_t buf1[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 't', 'e', 's', 't', 0x00}; +uint8_t buf2[] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 't', 'e', 's', 't', 0x00}; +uint8_t buf3[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 't', 'e', 's', 't', 0x00}; +uint8_t buf4[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 't', 'e', 's', 't', 'x'}; + +uint8_t buf_orig1[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 'k', 'e', 'y', 0x00, 'v', 'a', 'l', 'u', 'e', 0x00}; + +uint8_t buf_initgr[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0x00, 0x00}; +#elif (__BYTE_ORDER == __BIG_ENDIAN) +uint8_t buf1[] = {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 't', 'e', 's', 't', 0x00}; +uint8_t buf2[] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 't', 'e', 's', 't', 0x00}; +uint8_t buf3[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 't', 'e', 's', 't', 0x00}; +uint8_t buf4[] = {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 't', 'e', 's', 't', 'x'}; + +uint8_t buf_orig1[] = {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 'k', 'e', 'y', 0x00, 'v', 'a', 'l', 'u', 'e', 0x00}; + +uint8_t buf_initgr[] = {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde}; +#else + #error "unknow endianess" +#endif + +uint8_t buf_initgr_no_gr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +enum nss_status __wrap_sss_nss_make_request_timeout(enum sss_cli_command cmd, + struct sss_cli_req_data *rd, + int timeout, + uint8_t **repbuf, + size_t *replen, + int *errnop) +{ + struct sss_nss_make_request_test_data *d; + + d = sss_mock_ptr_type(struct sss_nss_make_request_test_data *); + + *replen = d->replen; + *errnop = d->errnop; + + /* the caller must be able to free repbuf. */ + if (*replen != 0 && d->repbuf != NULL) { + *repbuf = malloc(*replen); + assert_non_null(*repbuf); + memcpy(*repbuf, d->repbuf, *replen); + } + + return d->nss_status; +} + +void test_getsidbyname(void **state) +{ + int ret; + char *sid = NULL; + size_t c; + enum sss_id_type type; + + struct test_data { + struct sss_nss_make_request_test_data d; + int ret; + const char *str; + } d[] = { + {{buf1, sizeof(buf1), 0, NSS_STATUS_SUCCESS}, EOK, "test"}, + {{buf2, sizeof(buf2), 0, NSS_STATUS_SUCCESS}, EBADMSG, NULL}, + {{buf3, sizeof(buf3), 0, NSS_STATUS_SUCCESS}, ENOENT, NULL}, + {{buf4, sizeof(buf4), 0, NSS_STATUS_SUCCESS}, EBADMSG, NULL}, + {{NULL, 0, 0, 0}, 0, NULL} + }; + + ret = sss_nss_getsidbyname(NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_nss_getsidbyname("", NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_nss_getsidbyname("", &sid, NULL); + assert_int_equal(ret, EINVAL); + free(sid); + sid = NULL; + + for (c = 0; d[c].d.repbuf != NULL; c++) { + will_return(__wrap_sss_nss_make_request_timeout, &d[c].d); + + ret = sss_nss_getsidbyname("test", &sid, &type); + assert_int_equal(ret, d[c].ret); + if (ret == EOK) { + assert_string_equal(sid, d[c].str); + assert_int_equal(type, 0); + } + free(sid); + sid = NULL; + } +} + +void test_getorigbyname(void **state) +{ + int ret; + struct sss_nss_kv *kv_list; + enum sss_id_type type; + struct sss_nss_make_request_test_data d = {buf_orig1, sizeof(buf_orig1), 0, NSS_STATUS_SUCCESS}; + + will_return(__wrap_sss_nss_make_request_timeout, &d); + ret = sss_nss_getorigbyname("test", &kv_list, &type); + assert_int_equal(ret, EOK); + assert_int_equal(type, SSS_ID_TYPE_UID); + assert_string_equal(kv_list[0].key, "key"); + assert_string_equal(kv_list[0].value, "value"); + assert_null(kv_list[1].key); + assert_null(kv_list[1].value); + + sss_nss_free_kv(kv_list); +} + +void test_sss_nss_getgrouplist_timeout(void **state) +{ + int ret; + gid_t groups[10]; + int ngroups = sizeof(groups); + struct sss_nss_make_request_test_data d = {buf_initgr, sizeof(buf_initgr), 0, NSS_STATUS_SUCCESS}; + + will_return(__wrap_sss_nss_make_request_timeout, &d); + ret = sss_nss_getgrouplist_timeout("test", 111, groups, &ngroups, 0, 0); + assert_int_equal(ret, EOK); + assert_int_equal(ngroups, 2); + assert_int_equal(groups[0], 111); + assert_int_equal(groups[1], 222); + + d.repbuf = buf_initgr_no_gr; + d.replen = sizeof(buf_initgr_no_gr); + + will_return(__wrap_sss_nss_make_request_timeout, &d); + ret = sss_nss_getgrouplist_timeout("test", 111, groups, &ngroups, 0, 0); + assert_int_equal(ret, EOK); + assert_int_equal(ngroups, 1); + assert_int_equal(groups[0], 111); +} + +int main(int argc, const char *argv[]) +{ + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_getsidbyname), + cmocka_unit_test(test_getorigbyname), + cmocka_unit_test(test_sss_nss_getgrouplist_timeout), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_ad_access_filter.c b/src/tests/cmocka/test_ad_access_filter.c new file mode 100644 index 0000000..9e6ecd0 --- /dev/null +++ b/src/tests/cmocka/test_ad_access_filter.c @@ -0,0 +1,361 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: AD access control filter tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <unistd.h> +#include <sys/types.h> +#include <ifaddrs.h> +#include <arpa/inet.h> + +/* In order to access opaque types */ +#include "providers/ad/ad_access.c" + +#include "tests/cmocka/common_mock.h" + +#define DOM_NAME "parent_dom" + +struct ad_access_test_ctx { + struct sss_domain_info *dom; +}; + +static struct ad_access_test_ctx *test_ctx; + +int ad_access_filter_test_setup(void **state) +{ + assert_true(leak_check_setup()); + test_ctx = talloc_zero(global_talloc_context, + struct ad_access_test_ctx); + assert_non_null(test_ctx); + + test_ctx->dom = talloc_zero(test_ctx, struct sss_domain_info); + assert_non_null(test_ctx->dom); + + test_ctx->dom->name = talloc_strdup(test_ctx->dom, DOM_NAME); + assert_non_null(test_ctx->dom->name); + return 0; +} + +int ad_access_filter_test_teardown(void **state) +{ + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +struct filter_parse_result { + const int result; + const char *best_match; +}; + +static void test_parse_filter_generic(const char *filter_in, + struct filter_parse_result *expected) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx; + char *best_match; + + assert_non_null(expected); + + tmp_ctx = talloc_new(global_talloc_context); + assert_non_null(tmp_ctx); + check_leaks_push(tmp_ctx); + + ret = ad_parse_access_filter(tmp_ctx, test_ctx->dom, filter_in, + &best_match); + assert_int_equal(ret, expected->result); + if (expected->result != EOK) { + goto done; + } + + if (expected->best_match != NULL) { + assert_string_equal(best_match, expected->best_match); + } else { + assert_true(best_match == NULL); + } + talloc_free(best_match); + +done: + assert_true(check_leaks_pop(tmp_ctx) == true); + talloc_free(tmp_ctx); +} + +/* Test that setting no filter lets all access through + */ +void test_no_filter(void **state) +{ + struct filter_parse_result expected = { + .result = EOK, + .best_match = NULL + }; + + test_parse_filter_generic(NULL, &expected); +} + +/* Test that if one filter is provided, it is returned as-is + */ +void test_single_filter(void **state) +{ + struct filter_parse_result expected = { + .result = EOK, + .best_match = "(name=foo)" + }; + + test_parse_filter_generic("name=foo", &expected); + test_parse_filter_generic("(name=foo)", &expected); + test_parse_filter_generic(DOM_NAME":(name=foo)", &expected); + test_parse_filter_generic("DOM:"DOM_NAME":(name=foo)", &expected); +} + +/* Test that if more filters are provided, the best match is returned */ +void test_filter_order(void **state) +{ + struct filter_parse_result expected = { + .result = EOK, + .best_match = "(name=foo)" + }; + + test_parse_filter_generic("name=foo?name=bar", &expected); + test_parse_filter_generic(DOM_NAME":(name=foo)?name=bar", &expected); + test_parse_filter_generic("name=bla?"DOM_NAME":(name=foo)?name=bar", &expected); + /* Test that another foreign domain wouldn't match */ + test_parse_filter_generic("anotherdom:(name=bla)?"DOM_NAME":(name=foo)", &expected); + test_parse_filter_generic("anotherdom:(name=bla)?(name=foo)", &expected); +} + +void test_filter_no_match(void **state) +{ + struct filter_parse_result expected = { + .result = EOK, + .best_match = NULL + }; + + test_parse_filter_generic("anotherdom:(name=bla)?yetanother:(name=foo)", &expected); +} + + +int parse_test_setup(void **state) +{ + assert_true(leak_check_setup()); + return 0; +} + +int parse_test_teardown(void **state) +{ + assert_true(leak_check_teardown()); + return 0; +} + +struct parse_result { + const int result; + const char *filter; + const char *spec; + const int flags; +}; + +static void test_parse_generic(const char *filter_in, struct parse_result *expected) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx; + char *filter; + char *spec; + int flags; + + assert_non_null(expected); + + tmp_ctx = talloc_new(global_talloc_context); + assert_non_null(tmp_ctx); + check_leaks_push(tmp_ctx); + + ret = parse_filter(tmp_ctx, filter_in, &filter, &spec, &flags); + + assert_int_equal(ret, expected->result); + if (expected->result != EOK) { + goto done; + } + + if (expected->filter != NULL) { + assert_string_equal(filter, expected->filter); + } else { + assert_true(filter == NULL); + } + talloc_free(filter); + + if (expected->spec != NULL) { + assert_string_equal(spec, expected->spec); + } else { + assert_true(spec == NULL); + } + talloc_free(spec); + + assert_int_equal(flags, expected->flags); + +done: + assert_true(check_leaks_pop(tmp_ctx) == true); + talloc_free(tmp_ctx); +} + +void test_parse_plain(void **state) +{ + struct parse_result expected = { + .result = EOK, + .filter = "name=foo", + .spec = NULL, + .flags = AD_FILTER_GENERIC + }; + + test_parse_generic("name=foo", &expected); +} + +void test_parse_dom_without_kw(void **state) +{ + struct parse_result expected = { + .result = EOK, + .filter = "(name=foo)", + .spec = "mydom", + .flags = AD_FILTER_DOMAIN + }; + + test_parse_generic("mydom:(name=foo)", &expected); + + /* Check we can handle domain called DOM */ + struct parse_result expected2 = { + .result = EOK, + .filter = "(name=foo)", + .spec = "DOM", + .flags = AD_FILTER_DOMAIN + }; + + test_parse_generic("DOM:(name=foo)", &expected2); +} + +void test_parse_dom_kw(void **state) +{ + struct parse_result expected = { + .result = EOK, + .filter = "(name=foo)", + .spec = "mydom", + .flags = AD_FILTER_DOMAIN + }; + + test_parse_generic("DOM:mydom:(name=foo)", &expected); +} + +void test_parse_forest_kw(void **state) +{ + struct parse_result expected = { + .result = EOK, + .filter = "(name=foo)", + .spec = "myforest", + .flags = AD_FILTER_FOREST + }; + + test_parse_generic("FOREST:myforest:(name=foo)", &expected); +} + + +void test_parse_malformed(void **state) +{ + struct parse_result expected = { + .result = EINVAL, + }; + + test_parse_generic("DOM:", &expected); + test_parse_generic("DOM::", &expected); + test_parse_generic("DOM:mydom:", &expected); + test_parse_generic("DOM:mydom:name=foo", &expected); + test_parse_generic("DOM::(name=foo)", &expected); + test_parse_generic("BLABLABLA:mydom:name=foo", &expected); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_parse_plain, + parse_test_setup, + parse_test_teardown), + + cmocka_unit_test_setup_teardown(test_parse_dom_without_kw, + parse_test_setup, + parse_test_teardown), + + cmocka_unit_test_setup_teardown(test_parse_dom_kw, + parse_test_setup, + parse_test_teardown), + + cmocka_unit_test_setup_teardown(test_parse_forest_kw, + parse_test_setup, + parse_test_teardown), + + cmocka_unit_test_setup_teardown(test_parse_malformed, + parse_test_setup, + parse_test_teardown), + + cmocka_unit_test_setup_teardown(test_no_filter, + ad_access_filter_test_setup, + ad_access_filter_test_teardown), + + cmocka_unit_test_setup_teardown(test_single_filter, + ad_access_filter_test_setup, + ad_access_filter_test_teardown), + + cmocka_unit_test_setup_teardown(test_filter_order, + ad_access_filter_test_setup, + ad_access_filter_test_teardown), + + cmocka_unit_test_setup_teardown(test_filter_no_match, + ad_access_filter_test_setup, + ad_access_filter_test_teardown), + + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c new file mode 100644 index 0000000..4c61954 --- /dev/null +++ b/src/tests/cmocka/test_ad_common.c @@ -0,0 +1,1262 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: AD access control filter tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <unistd.h> +#include <sys/types.h> +#include <ifaddrs.h> +#include <arpa/inet.h> + +#include "providers/ad/ad_pac.h" +#include "util/crypto/sss_crypto.h" +#include "util/util_sss_idmap.h" + +/* In order to access opaque types */ +#include "providers/ad/ad_common.c" + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_krb5.h" + +#define DOMNAME "domname" +#define SUBDOMNAME "sub."DOMNAME +#define REALMNAME DOMNAME +#define HOST_NAME "ad."REALMNAME + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_AUTHID "host/"HOST_NAME +#define KEYTAB_TEST_PRINC TEST_AUTHID"@"REALMNAME +#define KEYTAB_PATH TESTS_PATH"/keytab_test.keytab" + +#define ONEWAY_DOMNAME "ONEWAY" +#define ONEWAY_HOST_NAME "ad."ONEWAY_DOMNAME + +#define ONEWAY_KEYTAB_PATH TESTS_PATH"/oneway_test.keytab" +#define ONEWAY_AUTHID "host/"ONEWAY_HOST_NAME +#define ONEWAY_TEST_PRINC ONEWAY_AUTHID"@"ONEWAY_DOMNAME + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_ad_sysdb.ldb" +#define TEST_ID_PROVIDER "ad" +#define TEST_DOM1_NAME "test_sysdb_subdomains_1" +#define TEST_DOM2_NAME "child2.test_sysdb_subdomains_2" +#define TEST_USER "test_user" + +static bool call_real_sasl_options; + +const char *domains[] = { TEST_DOM1_NAME, + TEST_DOM2_NAME, + NULL }; +struct ad_sysdb_test_ctx { + struct sss_test_ctx *tctx; +}; + +static int test_ad_sysdb_setup(void **state) +{ + struct ad_sysdb_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, + struct ad_sysdb_test_ctx); + assert_non_null(test_ctx); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains, + TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + *state = test_ctx; + return 0; +} + +static int test_ad_sysdb_teardown(void **state) +{ + struct ad_sysdb_test_ctx *test_ctx = + talloc_get_type(*state, struct ad_sysdb_test_ctx); + + test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_check_if_pac_is_available(void **state) +{ + int ret; + struct ad_sysdb_test_ctx *test_ctx = + talloc_get_type(*state, struct ad_sysdb_test_ctx); + struct dp_id_data *ar; + struct ldb_message *msg = NULL; + struct sysdb_attrs *attrs; + + ret = check_if_pac_is_available(NULL, NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ar = talloc_zero(test_ctx, struct dp_id_data); + assert_non_null(ar); + + ret = check_if_pac_is_available(test_ctx, test_ctx->tctx->dom, ar, &msg); + assert_int_equal(ret, EINVAL); + + ar->filter_type = BE_FILTER_NAME; + ar->filter_value = discard_const(TEST_USER); + + ret = check_if_pac_is_available(test_ctx, test_ctx->tctx->dom, ar, &msg); + assert_int_equal(ret, ENOENT); + + ret = sysdb_add_user(test_ctx->tctx->dom, TEST_USER, 123, 456, NULL, NULL, + NULL, NULL, NULL, 0, 0); + assert_int_equal(ret, EOK); + + ret = check_if_pac_is_available(test_ctx, test_ctx->tctx->dom, ar, &msg); + assert_int_equal(ret, ENOENT); + + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_PAC_BLOB, "pac"); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(test_ctx->tctx->dom, TEST_USER, attrs, + SYSDB_MOD_REP); + + /* PAC available but too old */ + ret = check_if_pac_is_available(test_ctx, test_ctx->tctx->dom, ar, &msg); + assert_int_equal(ret, ENOENT); + + talloc_free(attrs); + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_PAC_BLOB_EXPIRE, 123); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(test_ctx->tctx->dom, TEST_USER, attrs, + SYSDB_MOD_REP); + + /* PAC available but still too old */ + ret = check_if_pac_is_available(test_ctx, test_ctx->tctx->dom, ar, &msg); + assert_int_equal(ret, ENOENT); + + talloc_free(attrs); + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_PAC_BLOB_EXPIRE, time(NULL) + 10); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(test_ctx->tctx->dom, TEST_USER, attrs, + SYSDB_MOD_REP); + + /* PAC available but still too old */ + ret = check_if_pac_is_available(test_ctx, test_ctx->tctx->dom, ar, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + assert_string_equal(ldb_msg_find_attr_as_string(msg, SYSDB_NAME, "x"), + TEST_USER); + + talloc_free(attrs); + talloc_free(ar); +} + +#define TEST_PAC_BASE64 \ + "BQAAAAAAAAABAAAA6AEAAFgAAAAAAAAACgAAABAAAABAAgAAAA" \ + "AAAAwAAAA4AAAAUAIAAAAAAAAGAAAAFAAAAIgCAAAAAAAABwAA" \ + "ABQAAACgAgAAAAAAAAEQCADMzMzM2AEAAAAAAAAAAAIA2hr35p" \ + "Ji0QH/////////f/////////9/4veKrwAP0AHit/TZyQ/QAf//" \ + "//////9/BgAGAAQAAgAGAAYACAACAAAAAAAMAAIAAAAAABAAAg" \ + "AAAAAAFAACAAAAAAAYAAIATwAAAFAEAAABAgAABQAAABwAAgAg" \ + "AAAAAAAAAAAAAAAAAAAAAAAAABIAFAAgAAIABAAGACQAAgAoAA" \ + "IAAAAAAAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \ + "AAAAAAEAAAAsAAIAAAAAAAAAAAAAAAAAAwAAAAAAAAADAAAAdA" \ + "B1ADEAAAADAAAAAAAAAAMAAAB0ACAAdQAAAAAAAAAAAAAAAAAA" \ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \ + "UAAAD9ogAABwAAAAECAAAHAAAAXAQAAAcAAABWBAAABwAAAImm" \ + "AAAHAAAACgAAAAAAAAAJAAAAQQBEAC0AUwBFAFIAVgBFAFIAAA" \ + "ADAAAAAAAAAAIAAABBAEQABAAAAAEEAAAAAAAFFQAAAPgSE9xH" \ + "8xx2Ry8u1wEAAAAwAAIABwAAAAUAAAABBQAAAAAABRUAAAApyU" \ + "/ZwjzDeDZVh/hUBAAAgD5SqNxk0QEGAHQAdQAxABgAEAAQACgA" \ + "AAAAAAAAAAB0AHUAMQBAAGEAZAAuAGQAZQB2AGUAbABBAEQALg" \ + "BEAEUAVgBFAEwAdv///4yBQZ5ZQnp3qwj2lKGcd0UAAAAAdv//" \ + "/39fn4UneD5l6YxP8w/U0coAAAAA" + +#define TEST_PAC_RESOURCE_GROUPS_BASE64 \ + "BQAAAAAAAAABAAAA8AEAAFgAAAAAAAAACgAAABQAAABIAgAA" \ + "AAAAAAwAAABYAAAAYAIAAAAAAAAGAAAAEAAAALgCAAAAAAAA" \ + "BwAAABQAAADIAgAAAAAAAAEQCADMzMzM4AEAAAAAAAAAAAIA" \ + "Rr0gPUQO1AH/////////f/////////9/TRPNRwtu0wFN0zZy" \ + "1G7TAf////////9/CgAKAAQAAgAKAAoACAACAAAAAAAMAAIA" \ + "AAAAABAAAgAAAAAAFAACAAAAAAAYAAIACwAAAFEEAAABAgAA" \ + "AwAAABwAAgAgAgAAAAAAAAAAAAAAAAAAAAAAAAQABgAgAAIA" \ + "BgAIACQAAgAoAAIAAAAAAAAAAAAQAgAAAAAAAAAAAAAAAAAA" \ + "AAAAAAAAAAAAAAAAAAAAAAEAAAAsAAIANAACAAEAAAA4AAIA" \ + "BQAAAAAAAAAFAAAAdAB1AHMAZQByAAAABQAAAAAAAAAFAAAA" \ + "dAB1AHMAZQByAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAECAAAHAAAA" \ + "YgQAAAcAAABjBAAABwAAAAMAAAAAAAAAAgAAAEQAQwAEAAAA" \ + "AAAAAAMAAABXAEkATgAAAAQAAAABBAAAAAAABRUAAAAkYm0r" \ + "SyFumd73jX0BAAAAMAACAAcAAAABAAAAAQEAAAAAABIBAAAA" \ + "BAAAAAEEAAAAAAAFFQAAACRibStLIW6Z3veNfQEAAABoBAAA" \ + "BwAAIAAAAACAEuVfRA7UAQoAdAB1AHMAZQByAAAAAAAoABAA" \ + "HAA4AAAAAAAAAAAAdAB1AHMAZQByAEAAdwBpAG4ALgB0AHIA" \ + "dQBzAHQALgB0AGUAcwB0AFcASQBOAC4AVABSAFUAUwBUAC4A" \ + "VABFAFMAVAAAAAAAEAAAAOGTj7I9Qn7XebOqdHb///+fHhrZ" \ + "kBt0So4jOFBk84sDAAAAAA==" + +#define TEST_PAC_WITH_HAS_SAM_NAME_AND_SID_BASE64 \ + "BgAAAAAAAAABAAAA2AEAAGgAAAAAAAAACgAAABgAAABAAgAA" \ + "AAAAAAwAAACIAAAAWAIAAAAAAAAGAAAAEAAAAOACAAAAAAAA" \ + "BwAAABAAAADwAgAAAAAAABAAAAAQAAAAAAMAAAAAAAABEAgA" \ + "zMzMzMgBAAAAAAAAAAACAHQG3etmONgB/////////3//////" \ + "////f1IR75ZZV9cBUtFYwSJY1wH/////////fw4ADgAEAAIA" \ + "DgAOAAgAAgAAAAAADAACAAAAAAAQAAIAAAAAABQAAgAAAAAA" \ + "GAACACwAAQDoAwAAAQIAAAEAAAAcAAIAIAAAAAAAAAAAAAAA" \ + "AAAAAAAAAAAWABgAIAACAA4AEAAkAAIAKAACAAAAAAAAAAAA" \ + "EAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA" \ + "LAACAAAAAAAAAAAAAAAAAAcAAAAAAAAABwAAAHYAYQBnAHIA" \ + "YQBuAHQAAAAHAAAAAAAAAAcAAABWAGEAZwByAGEAbgB0AAAA" \ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \ + "AAAAAAAAAAAAAAAAAQAAAAECAAAHAAAADAAAAAAAAAALAAAA" \ + "TwBUAEgARQBSAC0AQQBEAC0ARABDAAAACAAAAAAAAAAHAAAA" \ + "TwBUAEgARQBSAEEARAAAAAQAAAABBAAAAAAABRUAAADjfjpn" \ + "NKqt8DV6HtgBAAAAMAACAAcAAAABAAAAAQEAAAAAABIBAAAA" \ + "gOAL92Y42AEOAHYAYQBnAHIAYQBuAHQAJgAYABYAQAADAAAA" \ + "DgBYABwAaAAAAAAAdgBhAGcAcgBhAG4AdABAAG8AdABoAGUA" \ + "cgAtAGEAZAAuAHYAbQAAAE8AVABIAEUAUgAtAEEARAAuAFYA" \ + "TQAAAHYAYQBnAHIAYQBuAHQAAAABBQAAAAAABRUAAADjfjpn" \ + "NKqt8DV6HtjoAwAAAAAAABAAAACHjVhlIcvUmxiq0L8QAAAA" \ + "yYHF7QwPjMVsbvTCEAAAAEr+xJAskwH6q5I2uw==" + +static void test_ad_get_data_from_pac(void **state) +{ + int ret; + struct PAC_LOGON_INFO *logon_info; + struct PAC_UPN_DNS_INFO *upn_dns_info; + uint8_t *test_pac_blob; + size_t test_pac_blob_size; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + + test_pac_blob = sss_base64_decode(test_ctx, TEST_PAC_BASE64, + &test_pac_blob_size); + assert_non_null(test_pac_blob_size); + + ret = ad_get_data_from_pac(test_ctx, 0, test_pac_blob, test_pac_blob_size, + &logon_info, &upn_dns_info); + assert_int_equal(ret, EOK); + assert_non_null(logon_info); + assert_string_equal(logon_info->info3.base.account_name.string, "tu1"); + assert_string_equal(logon_info->info3.base.full_name.string, "t u"); + assert_int_equal(logon_info->info3.base.rid, 1104); + assert_int_equal(logon_info->info3.base.primary_gid, 513); + assert_int_equal(logon_info->info3.base.groups.count, 5); + assert_string_equal(logon_info->info3.base.logon_domain.string, "AD"); + assert_int_equal(logon_info->info3.sidcount, 1); + + assert_non_null(upn_dns_info); + assert_string_equal(upn_dns_info->upn_name, "tu1@ad.devel"); + assert_string_equal(upn_dns_info->dns_domain_name, "AD.DEVEL"); + assert_int_equal(upn_dns_info->flags, 0); + + talloc_free(logon_info); + talloc_free(upn_dns_info); + + ret = ad_get_data_from_pac(test_ctx, CHECK_PAC_UPN_DNS_INFO_PRESENT, + test_pac_blob, test_pac_blob_size, + &logon_info, &upn_dns_info); + assert_int_equal(ret, EOK); + talloc_free(logon_info); + talloc_free(upn_dns_info); + + ret = ad_get_data_from_pac(test_ctx, CHECK_PAC_CHECK_UPN_DNS_INFO_EX, + test_pac_blob, test_pac_blob_size, + &logon_info, &upn_dns_info); + assert_int_equal(ret, EOK); + talloc_free(logon_info); + talloc_free(upn_dns_info); + + ret = ad_get_data_from_pac(test_ctx, CHECK_PAC_UPN_DNS_INFO_EX_PRESENT, + test_pac_blob, test_pac_blob_size, + &logon_info, &upn_dns_info); + assert_int_equal(ret, ERR_CHECK_PAC_FAILED); + assert_null(logon_info); + assert_null(upn_dns_info); + + talloc_free(test_pac_blob); +} + +static void test_ad_get_sids_from_pac(void **state) +{ + int ret; + struct PAC_LOGON_INFO *logon_info; + struct PAC_UPN_DNS_INFO *upn_dns_info; + uint8_t *test_pac_blob; + size_t test_pac_blob_size; + char *user_sid; + char *primary_group_sid; + size_t num_sids; + char **sid_list; + struct sss_idmap_ctx *idmap_ctx; + enum idmap_error_code err; + size_t c; + size_t s; + + const char *sid_check_list[] = { "S-1-5-21-3692237560-1981608775-3610128199-513", + "S-1-5-21-3692237560-1981608775-3610128199-1110", + "S-1-5-21-3692237560-1981608775-3610128199-1116", + "S-1-5-21-3692237560-1981608775-3610128199-41725", + "S-1-5-21-3692237560-1981608775-3610128199-42633", + "S-1-5-21-3645884713-2026060994-4169618742-1108", + NULL }; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + + err = sss_idmap_init(sss_idmap_talloc, test_ctx, sss_idmap_talloc_free, + &idmap_ctx); + assert_int_equal(err, IDMAP_SUCCESS); + + test_pac_blob = sss_base64_decode(test_ctx, TEST_PAC_BASE64, + &test_pac_blob_size); + assert_non_null(test_pac_blob_size); + + ret = ad_get_data_from_pac(test_ctx, 0, test_pac_blob, test_pac_blob_size, + &logon_info, &upn_dns_info); + assert_int_equal(ret, EOK); + + ret = ad_get_sids_from_pac(test_ctx, idmap_ctx, logon_info, &user_sid, + &primary_group_sid, &num_sids, &sid_list); + assert_int_equal(ret, EOK); + assert_string_equal(user_sid, + "S-1-5-21-3692237560-1981608775-3610128199-1104"); + assert_string_equal(primary_group_sid, + "S-1-5-21-3692237560-1981608775-3610128199-513"); + assert_int_equal(num_sids, 6); + + for (c = 0; sid_check_list[c] != NULL; c++) { + for (s = 0; s < num_sids; s++) { + if (strcmp(sid_check_list[c], sid_list[s]) == 0) { + break; + } + } + if (s == num_sids) { + fail_msg("SID [%s] not found in SID list.", sid_check_list[c]); + } + } + + assert_non_null(upn_dns_info); + assert_string_equal(upn_dns_info->upn_name, "tu1@ad.devel"); + assert_string_equal(upn_dns_info->dns_domain_name, "AD.DEVEL"); + assert_int_equal(upn_dns_info->flags, 0); + + talloc_free(test_pac_blob); + talloc_free(logon_info); + talloc_free(upn_dns_info); + talloc_free(user_sid); + talloc_free(primary_group_sid); + talloc_free(sid_list); + sss_idmap_free(idmap_ctx); +} + +#ifdef HAVE_STRUCT_PAC_LOGON_INFO_RESOURCE_GROUPS +static void test_ad_get_sids_from_pac_with_resource_groups(void **state) +{ + int ret; + struct PAC_LOGON_INFO *logon_info; + struct PAC_UPN_DNS_INFO *upn_dns_info; + uint8_t *test_pac_blob; + size_t test_pac_blob_size; + char *user_sid; + char *primary_group_sid; + size_t num_sids; + char **sid_list; + struct sss_idmap_ctx *idmap_ctx; + enum idmap_error_code err; + size_t c; + size_t s; + + const char *sid_check_list[] = { "S-1-5-21-728588836-2574131531-2106456030-513", + "S-1-5-21-728588836-2574131531-2106456030-1122", + "S-1-5-21-728588836-2574131531-2106456030-1123", + "S-1-5-21-728588836-2574131531-2106456030-1128", + "S-1-18-1", + NULL }; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + + err = sss_idmap_init(sss_idmap_talloc, test_ctx, sss_idmap_talloc_free, + &idmap_ctx); + assert_int_equal(err, IDMAP_SUCCESS); + + test_pac_blob = sss_base64_decode(test_ctx, TEST_PAC_RESOURCE_GROUPS_BASE64, + &test_pac_blob_size); + assert_non_null(test_pac_blob_size); + + ret = ad_get_data_from_pac(test_ctx, 0, test_pac_blob, test_pac_blob_size, + &logon_info, &upn_dns_info); + assert_int_equal(ret, EOK); + + ret = ad_get_sids_from_pac(test_ctx, idmap_ctx, logon_info, &user_sid, + &primary_group_sid, &num_sids, &sid_list); + assert_int_equal(ret, EOK); + assert_string_equal(user_sid, + "S-1-5-21-728588836-2574131531-2106456030-1105"); + assert_string_equal(primary_group_sid, + "S-1-5-21-728588836-2574131531-2106456030-513"); + assert_int_equal(num_sids, 5); + + for (c = 0; sid_check_list[c] != NULL; c++) { + for (s = 0; s < num_sids; s++) { + if (strcmp(sid_check_list[c], sid_list[s]) == 0) { + break; + } + } + if (s == num_sids) { + fail_msg("SID [%s] not found in SID list.", sid_check_list[c]); + } + } + + assert_non_null(upn_dns_info); + assert_string_equal(upn_dns_info->upn_name, "tuser@win.trust.test"); + assert_string_equal(upn_dns_info->dns_domain_name, "WIN.TRUST.TEST"); + assert_int_equal(upn_dns_info->flags, 0); + + talloc_free(test_pac_blob); + talloc_free(logon_info); + talloc_free(upn_dns_info); + talloc_free(user_sid); + talloc_free(primary_group_sid); + talloc_free(sid_list); + sss_idmap_free(idmap_ctx); +} +#endif + +#ifdef HAVE_STRUCT_PAC_UPN_DNS_INFO_EX +static void test_ad_pac_with_has_sam_name_and_sid(void **state) +{ + int ret; + struct PAC_LOGON_INFO *logon_info; + struct PAC_UPN_DNS_INFO *upn_dns_info; + uint8_t *test_pac_blob; + size_t test_pac_blob_size; + char *sid_str = NULL; + struct sss_idmap_ctx *idmap_ctx; + enum idmap_error_code err; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + + err = sss_idmap_init(sss_idmap_talloc, test_ctx, sss_idmap_talloc_free, + &idmap_ctx); + + assert_int_equal(err, IDMAP_SUCCESS); + + test_pac_blob = sss_base64_decode(test_ctx, TEST_PAC_WITH_HAS_SAM_NAME_AND_SID_BASE64, + &test_pac_blob_size); + assert_non_null(test_pac_blob_size); + + ret = ad_get_data_from_pac(test_ctx, + CHECK_PAC_UPN_DNS_INFO_PRESENT + |CHECK_PAC_CHECK_UPN_DNS_INFO_EX + |CHECK_PAC_UPN_DNS_INFO_EX_PRESENT, + test_pac_blob, test_pac_blob_size, + &logon_info, &upn_dns_info); + assert_int_equal(ret, EOK); + assert_non_null(logon_info); + assert_string_equal(logon_info->info3.base.account_name.string, "vagrant"); + assert_string_equal(logon_info->info3.base.full_name.string, "Vagrant"); + assert_int_equal(logon_info->info3.base.rid, 1000); + assert_int_equal(logon_info->info3.base.primary_gid, 513); + assert_int_equal(logon_info->info3.base.groups.count, 1); + assert_string_equal(logon_info->info3.base.logon_domain.string, "OTHERAD"); + assert_int_equal(logon_info->info3.sidcount, 1); + + assert_non_null(upn_dns_info); + assert_string_equal(upn_dns_info->upn_name, "vagrant@other-ad.vm"); + assert_string_equal(upn_dns_info->dns_domain_name, "OTHER-AD.VM"); + assert_int_equal(upn_dns_info->flags, 3); + assert_string_equal(upn_dns_info->ex.sam_name_and_sid.samaccountname, "vagrant"); + + sss_idmap_smb_sid_to_sid(idmap_ctx, upn_dns_info->ex.sam_name_and_sid.objectsid, &sid_str); + assert_string_equal(sid_str, "S-1-5-21-1731886819-4037913140-3625876021-1000"); + + talloc_free(test_pac_blob); + talloc_free(logon_info); + talloc_free(upn_dns_info); + talloc_free(sid_str); + sss_idmap_free(idmap_ctx); +} + +static void test_ad_pac_missing_upn_dns_info(void **state) +{ + int ret; + DATA_BLOB blob; + DATA_BLOB new_blob; + uint8_t *test_pac_blob; + size_t test_pac_blob_size; + struct PAC_BUFFER *pac_buffers; + struct PAC_DATA *pac_data; + struct ndr_pull *ndr_pull; + struct PAC_DATA *orig_pac_data; + enum ndr_err_code ndr_err; + size_t c; + TALLOC_CTX *tmp_ctx; + struct PAC_LOGON_INFO *logon_info; + struct PAC_UPN_DNS_INFO *upn_dns_info; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + test_pac_blob = sss_base64_decode(tmp_ctx, TEST_PAC_WITH_HAS_SAM_NAME_AND_SID_BASE64, + &test_pac_blob_size); + assert_non_null(test_pac_blob_size); + + blob.data = test_pac_blob; + blob.length = test_pac_blob_size; + + ndr_pull = ndr_pull_init_blob(&blob, tmp_ctx); + assert_non_null(ndr_pull); + ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; /* FIXME: is this really needed ? */ + + orig_pac_data = talloc_zero(tmp_ctx, struct PAC_DATA); + assert_non_null(orig_pac_data); + + ndr_err = ndr_pull_PAC_DATA(ndr_pull, NDR_SCALARS|NDR_BUFFERS, orig_pac_data); + assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err)); + + pac_buffers = talloc_array(tmp_ctx, struct PAC_BUFFER, 1); + assert_non_null(pac_buffers); + + pac_data = talloc_zero(tmp_ctx, struct PAC_DATA); + assert_non_null(pac_data); + + for(c = 0; c < orig_pac_data->num_buffers; c++) { + if (orig_pac_data->buffers[c].type == PAC_TYPE_LOGON_INFO) { + pac_buffers[0] = orig_pac_data->buffers[c]; + break; + } + } + + pac_data->num_buffers = 1; + pac_data->version = 0; + pac_data->buffers = pac_buffers; + + ndr_err = ndr_push_struct_blob(&new_blob, tmp_ctx, pac_data, + (ndr_push_flags_fn_t)ndr_push_PAC_DATA); + assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err)); + + ret = ad_get_data_from_pac(test_ctx, 0, + new_blob.data, new_blob.length, + &logon_info, &upn_dns_info); + assert_int_equal(ret, EOK); + assert_non_null(logon_info); + assert_null(upn_dns_info); + talloc_free(logon_info); + + ret = ad_get_data_from_pac(test_ctx, CHECK_PAC_UPN_DNS_INFO_PRESENT, + new_blob.data, new_blob.length, + &logon_info, &upn_dns_info); + assert_int_equal(ret, ERR_CHECK_PAC_FAILED); + assert_null(logon_info); + assert_null(upn_dns_info); + + ret = ad_get_data_from_pac(test_ctx, CHECK_PAC_UPN_DNS_INFO_EX_PRESENT, + new_blob.data, new_blob.length, + &logon_info, &upn_dns_info); + assert_int_equal(ret, ERR_CHECK_PAC_FAILED); + assert_null(logon_info); + assert_null(upn_dns_info); + + + talloc_free(pac_buffers); + talloc_free(pac_data); + talloc_free(orig_pac_data); + talloc_free(test_pac_blob); + talloc_free(ndr_pull); + talloc_free(tmp_ctx); +} + +#endif + +static void test_ad_get_pac_data_from_user_entry(void **state) +{ + int ret; + struct ldb_message *user_msg; + struct ldb_val val; + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + struct sss_idmap_ctx *idmap_ctx; + enum idmap_error_code err; + char *username; + char *user_sid; + char *primary_group_sid; + size_t num_sids; + char **sid_list; + size_t c; + size_t s; + const char *sid_check_list[] = { "S-1-5-21-3692237560-1981608775-3610128199-513", + "S-1-5-21-3692237560-1981608775-3610128199-1110", + "S-1-5-21-3692237560-1981608775-3610128199-1116", + "S-1-5-21-3692237560-1981608775-3610128199-41725", + "S-1-5-21-3692237560-1981608775-3610128199-42633", + "S-1-5-21-3645884713-2026060994-4169618742-1108", + NULL }; + + err = sss_idmap_init(sss_idmap_talloc, test_ctx, sss_idmap_talloc_free, + &idmap_ctx); + assert_int_equal(err, IDMAP_SUCCESS); + + user_msg = ldb_msg_new(test_ctx); + assert_non_null(user_msg); + + ret = ldb_msg_add_string(user_msg, SYSDB_NAME, "username"); + assert_int_equal(ret, EOK); + ret = ldb_msg_add_string(user_msg, SYSDB_OBJECTCATEGORY, SYSDB_USER_CLASS); + assert_int_equal(ret, EOK); + ret = ldb_msg_add_string(user_msg, SYSDB_PAC_BLOB_EXPIRE, "12345"); + assert_int_equal(ret, EOK); + val.data = sss_base64_decode(test_ctx, TEST_PAC_BASE64, &val.length); + ret = ldb_msg_add_value(user_msg, SYSDB_PAC_BLOB, &val, NULL); + assert_int_equal(ret, EOK); + + + ret = ad_get_pac_data_from_user_entry(test_ctx, user_msg, idmap_ctx, + &username, &user_sid, + &primary_group_sid, &num_sids, + &sid_list); + assert_int_equal(ret, EOK); + assert_string_equal(username, "username"); + assert_string_equal(user_sid, + "S-1-5-21-3692237560-1981608775-3610128199-1104"); + assert_string_equal(primary_group_sid, + "S-1-5-21-3692237560-1981608775-3610128199-513"); + assert_int_equal(num_sids, 6); + for (c = 0; sid_check_list[c] != NULL; c++) { + for (s = 0; s < num_sids; s++) { + if (strcmp(sid_check_list[c], sid_list[s]) == 0) { + break; + } + } + if (s == num_sids) { + fail_msg("SID [%s] not found in SID list.", sid_check_list[c]); + } + } + + talloc_free(username); + talloc_free(user_sid); + talloc_free(primary_group_sid); + talloc_free(sid_list); + talloc_free(val.data); + talloc_free(user_msg); + sss_idmap_free(idmap_ctx); +} + +krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id) +{ + return krb5_kt_resolve(context, KEYTAB_PATH, id); +} + +struct ad_common_test_ctx { + struct ad_id_ctx *ad_ctx; + struct ad_id_ctx *subdom_ad_ctx; + + struct sss_domain_info *dom; + struct sss_domain_info *subdom; +}; + +static int test_ad_common_setup(void **state) +{ + struct ad_common_test_ctx *test_ctx; + + test_dom_suite_setup(TESTS_PATH); + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct ad_common_test_ctx); + assert_non_null(test_ctx); + + test_ctx->dom = talloc_zero(test_ctx, struct sss_domain_info); + assert_non_null(test_ctx->dom); + test_ctx->dom->name = discard_const(DOMNAME); + + test_ctx->subdom = talloc_zero(test_ctx, struct sss_domain_info); + assert_non_null(test_ctx->subdom); + test_ctx->subdom->name = discard_const(SUBDOMNAME); + test_ctx->subdom->parent = test_ctx->dom; + + test_ctx->ad_ctx = talloc_zero(test_ctx, struct ad_id_ctx); + assert_non_null(test_ctx->ad_ctx); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int test_ad_common_teardown(void **state) +{ + int ret; + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + assert_non_null(test_ctx); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + + ret = rmdir(TESTS_PATH); + assert_return_code(ret, errno); + + return 0; +} + +static void test_ad_create_1way_trust_options(void **state) +{ + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + const char *s; + + call_real_sasl_options = true; + /* Make sure this is not the keytab that __wrap_krb5_kt_default uses */ + mock_keytab_with_contents(test_ctx, ONEWAY_KEYTAB_PATH, ONEWAY_TEST_PRINC); + + test_ctx->subdom->name = discard_const(ONEWAY_DOMNAME); + test_ctx->ad_ctx->ad_options = ad_create_1way_trust_options( + test_ctx->ad_ctx, + NULL, + NULL, + NULL, + test_ctx->subdom, + ONEWAY_HOST_NAME, + ONEWAY_KEYTAB_PATH, + ONEWAY_AUTHID); + assert_non_null(test_ctx->ad_ctx->ad_options); + + assert_int_equal(test_ctx->ad_ctx->ad_options->id->schema_type, + SDAP_SCHEMA_AD); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic, + AD_KRB5_REALM); + assert_non_null(s); + assert_string_equal(s, ONEWAY_DOMNAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic, + AD_DOMAIN); + assert_non_null(s); + assert_string_equal(s, ONEWAY_DOMNAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic, + AD_HOSTNAME); + assert_non_null(s); + assert_string_equal(s, ONEWAY_HOST_NAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic, + AD_KEYTAB); + assert_non_null(s); + assert_string_equal(s, ONEWAY_KEYTAB_PATH); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic, + SDAP_KRB5_KEYTAB); + assert_non_null(s); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic, + SDAP_SASL_REALM); + assert_non_null(s); + assert_string_equal(s, ONEWAY_DOMNAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic, + SDAP_KRB5_REALM); + assert_non_null(s); + assert_string_equal(s, ONEWAY_DOMNAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic, + SDAP_SASL_AUTHID); + assert_non_null(s); + assert_string_equal(s, ONEWAY_AUTHID); + + talloc_free(test_ctx->ad_ctx->ad_options); + + unlink(ONEWAY_KEYTAB_PATH); +} +static void test_ad_create_2way_trust_options(void **state) +{ + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + const char *s; + + call_real_sasl_options = true; + mock_keytab_with_contents(test_ctx, KEYTAB_PATH, KEYTAB_TEST_PRINC); + test_ctx->subdom->name = discard_const(DOMNAME); + + test_ctx->ad_ctx->ad_options = ad_create_2way_trust_options( + test_ctx->ad_ctx, + NULL, + NULL, + NULL, + REALMNAME, + test_ctx->subdom, + HOST_NAME, + NULL); + + assert_non_null(test_ctx->ad_ctx->ad_options); + + assert_int_equal(test_ctx->ad_ctx->ad_options->id->schema_type, + SDAP_SCHEMA_AD); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic, + AD_KRB5_REALM); + assert_non_null(s); + assert_string_equal(s, REALMNAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic, + AD_DOMAIN); + assert_non_null(s); + assert_string_equal(s, DOMNAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic, + AD_HOSTNAME); + assert_non_null(s); + assert_string_equal(s, HOST_NAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic, + SDAP_KRB5_KEYTAB); + assert_null(s); /* This is the system keytab */ + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic, + SDAP_SASL_REALM); + assert_non_null(s); + assert_string_equal(s, REALMNAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic, + SDAP_KRB5_REALM); + assert_non_null(s); + assert_string_equal(s, REALMNAME); + + s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic, + SDAP_SASL_AUTHID); + assert_non_null(s); + assert_string_equal(s, TEST_AUTHID); + + talloc_free(test_ctx->ad_ctx->ad_options); + + unlink(KEYTAB_PATH); +} + +static int +test_ldap_conn_setup(void **state) +{ + struct ad_common_test_ctx *test_ctx; + errno_t ret; + struct sdap_domain *sdom; + struct ad_id_ctx *ad_ctx; + struct ad_id_ctx *subdom_ad_ctx; + struct sdap_id_conn_ctx *subdom_ldap_ctx; + + ret = test_ad_common_setup((void **) &test_ctx); + assert_int_equal(ret, EOK); + + mock_keytab_with_contents(test_ctx, KEYTAB_PATH, KEYTAB_TEST_PRINC); + + ad_ctx = test_ctx->ad_ctx; + + test_ctx->ad_ctx->ad_options = ad_create_2way_trust_options( + ad_ctx, + NULL, + NULL, + NULL, + REALMNAME, + test_ctx->subdom, + HOST_NAME, + NULL); + + assert_non_null(ad_ctx->ad_options); + + ad_ctx->gc_ctx = talloc_zero(ad_ctx, struct sdap_id_conn_ctx); + assert_non_null(ad_ctx->gc_ctx); + + ad_ctx->ldap_ctx = talloc_zero(ad_ctx, struct sdap_id_conn_ctx); + assert_non_null(ad_ctx->ldap_ctx); + + ad_ctx->sdap_id_ctx = talloc_zero(ad_ctx, struct sdap_id_ctx); + assert_non_null(ad_ctx->sdap_id_ctx); + + ad_ctx->sdap_id_ctx->opts = talloc_zero(ad_ctx->sdap_id_ctx, + struct sdap_options); + assert_non_null(ad_ctx->sdap_id_ctx->opts); + + ret = sdap_domain_add(ad_ctx->sdap_id_ctx->opts, test_ctx->dom, &sdom); + assert_int_equal(ret, EOK); + sdom->pvt = ad_ctx; + + subdom_ad_ctx = talloc_zero(test_ctx, struct ad_id_ctx); + assert_non_null(subdom_ad_ctx); + + subdom_ldap_ctx = talloc_zero(subdom_ad_ctx, struct sdap_id_conn_ctx); + assert_non_null(subdom_ldap_ctx); + subdom_ad_ctx->ldap_ctx = subdom_ldap_ctx; + + ret = sdap_domain_add(ad_ctx->sdap_id_ctx->opts, test_ctx->subdom, &sdom); + assert_int_equal(ret, EOK); + sdom->pvt = subdom_ad_ctx; + + test_ctx->subdom_ad_ctx = subdom_ad_ctx; + + *state = test_ctx; + return 0; +} + +static int +test_ldap_conn_teardown(void **state) +{ + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + assert_non_null(test_ctx); + + unlink(KEYTAB_PATH); + + talloc_free(test_ctx->subdom_ad_ctx); + talloc_free(test_ctx->ad_ctx->ad_options); + talloc_free(test_ctx->ad_ctx->gc_ctx); + talloc_free(test_ctx->ad_ctx->ldap_ctx); + talloc_free(test_ctx->ad_ctx->sdap_id_ctx); + + test_ad_common_teardown((void **) &test_ctx); + return 0; +} + +errno_t +__real_sdap_set_sasl_options(struct sdap_options *id_opts, + char *default_primary, + char *default_realm, + const char *keytab_path); +errno_t +__wrap_sdap_set_sasl_options(struct sdap_options *id_opts, + char *default_primary, + char *default_realm, + const char *keytab_path) +{ + /* Pretend SASL is fine */ + if (call_real_sasl_options == true) { + return __real_sdap_set_sasl_options(id_opts, + default_primary, + default_realm, + keytab_path); + } + + return EOK; +} + +void test_ad_get_dom_ldap_conn(void **state) +{ + struct sdap_id_conn_ctx *conn; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + assert_non_null(test_ctx); + + conn = ad_get_dom_ldap_conn(test_ctx->ad_ctx, test_ctx->dom); + assert_true(conn == test_ctx->ad_ctx->ldap_ctx); + + conn = ad_get_dom_ldap_conn(test_ctx->ad_ctx, test_ctx->subdom); + assert_true(conn == test_ctx->subdom_ad_ctx->ldap_ctx); +} + +void test_gc_conn_list(void **state) +{ + struct sdap_id_conn_ctx **conn_list; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + assert_non_null(test_ctx); + + assert_true(dp_opt_get_bool(test_ctx->ad_ctx->ad_options->basic, + AD_ENABLE_GC)); + conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->dom); + assert_non_null(conn_list); + + assert_true(conn_list[0] == test_ctx->ad_ctx->gc_ctx); + /* If there is a fallback, we should ignore the offline mode */ + assert_true(conn_list[0]->ignore_mark_offline); + assert_true(conn_list[1] == test_ctx->ad_ctx->ldap_ctx); + assert_false(conn_list[1]->ignore_mark_offline); + assert_null(conn_list[2]); + talloc_free(conn_list); + + conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->subdom); + assert_non_null(conn_list); + + assert_true(conn_list[0] == test_ctx->ad_ctx->gc_ctx); + assert_true(conn_list[0]->ignore_mark_offline); + assert_true(conn_list[1] == test_ctx->subdom_ad_ctx->ldap_ctx); + /* Subdomain error should not set the backend offline! */ + assert_true(conn_list[1]->ignore_mark_offline); + talloc_free(conn_list); + + dp_opt_set_bool(test_ctx->ad_ctx->ad_options->basic, AD_ENABLE_GC, false); + assert_false(dp_opt_get_bool(test_ctx->ad_ctx->ad_options->basic, + AD_ENABLE_GC)); + + conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->dom); + assert_non_null(conn_list); + + assert_true(conn_list[0] == test_ctx->ad_ctx->ldap_ctx); + assert_false(conn_list[0]->ignore_mark_offline); + assert_null(conn_list[1]); + talloc_free(conn_list); + + conn_list = ad_gc_conn_list(test_ctx, test_ctx->ad_ctx, test_ctx->subdom); + assert_non_null(conn_list); + + assert_true(conn_list[0] == test_ctx->subdom_ad_ctx->ldap_ctx); + assert_true(conn_list[0]->ignore_mark_offline); + assert_null(conn_list[1]); + talloc_free(conn_list); +} + +void test_ldap_conn_list(void **state) +{ + struct sdap_id_conn_ctx **conn_list; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + assert_non_null(test_ctx); + + conn_list = ad_ldap_conn_list(test_ctx, + test_ctx->ad_ctx, + test_ctx->dom); + assert_non_null(conn_list); + + assert_true(conn_list[0] == test_ctx->ad_ctx->ldap_ctx); + assert_false(conn_list[0]->ignore_mark_offline); + assert_null(conn_list[1]); + talloc_free(conn_list); + + conn_list = ad_ldap_conn_list(test_ctx, + test_ctx->ad_ctx, + test_ctx->subdom); + assert_non_null(conn_list); + + assert_true(conn_list[0] == test_ctx->subdom_ad_ctx->ldap_ctx); + assert_true(conn_list[0]->ignore_mark_offline); + assert_null(conn_list[1]); + talloc_free(conn_list); +} + +void test_user_conn_list(void **state) +{ + struct sdap_id_conn_ctx **conn_list; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + assert_non_null(test_ctx); + + conn_list = ad_user_conn_list(test_ctx, test_ctx->ad_ctx, + test_ctx->dom); + assert_non_null(conn_list); + + assert_true(conn_list[0] == test_ctx->ad_ctx->ldap_ctx); + assert_false(conn_list[0]->ignore_mark_offline); + assert_null(conn_list[1]); + talloc_free(conn_list); + + conn_list = ad_user_conn_list(test_ctx, test_ctx->ad_ctx, + test_ctx->subdom); + assert_non_null(conn_list); + + assert_true(conn_list[0] == test_ctx->ad_ctx->gc_ctx); + assert_true(conn_list[0]->ignore_mark_offline); + assert_true(conn_list[1] == test_ctx->subdom_ad_ctx->ldap_ctx); + /* Subdomain error should not set the backend offline! */ + assert_true(conn_list[1]->ignore_mark_offline); + talloc_free(conn_list); +} + +void test_netlogon_get_domain_info(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + struct ldb_val val = { 0 }; + char *flat_name; + char *site; + char *forest; + + struct ad_common_test_ctx *test_ctx = talloc_get_type(*state, + struct ad_common_test_ctx); + assert_non_null(test_ctx); + + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + ret = netlogon_get_domain_info(test_ctx, attrs, false, NULL, NULL, NULL); + assert_int_equal(ret, ENOENT); + + ret = sysdb_attrs_add_val(attrs, AD_AT_NETLOGON, &val); + assert_int_equal(ret, EOK); + + ret = netlogon_get_domain_info(test_ctx, attrs, false, NULL, NULL, NULL); + assert_int_equal(ret, EBADMSG); + + talloc_free(attrs); + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + val.data = sss_base64_decode(test_ctx, "FwAAAP0zAABsGcIYI7j2TL97Rd+TvpATAmFkBWRldmVsAMAYCWFkLXNlcnZlcsAYAkFEAAlBRC1TRVJWRVIAABdEZWZhdWx0LUZpcnN0LVNpdGUtTmFtZQDAQAUAAAD/////", &val.length); + assert_non_null(val.data); + + ret = sysdb_attrs_add_val(attrs, AD_AT_NETLOGON, &val); + assert_int_equal(ret, EOK); + + ret = netlogon_get_domain_info(test_ctx, attrs, false, &flat_name, &site, &forest); + assert_int_equal(ret, EOK); + assert_string_equal(flat_name, "AD"); + assert_string_equal(site, "Default-First-Site-Name"); + assert_string_equal(forest, "ad.devel"); + + /* missing site */ + talloc_free(flat_name); + talloc_free(site); + talloc_free(forest); + talloc_free(val.data); + talloc_free(attrs); + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + val.data = sss_base64_decode(test_ctx, "FwAAAH0zAABsGcIYI7j2TL97Rd+TvpATAmFkBWRldmVsAMAYCWFkLXNlcnZlcsAYAkFEAAlBRC1TRVJWRVIAABdEZWZhdWx0LUZpcnN0LVNpdGUtTmFtZQAABQAAAP////8=", &val.length); + assert_non_null(val.data); + + ret = sysdb_attrs_add_val(attrs, AD_AT_NETLOGON, &val); + assert_int_equal(ret, EOK); + + ret = netlogon_get_domain_info(test_ctx, attrs, false, &flat_name, &site, &forest); + assert_int_equal(ret, EOK); + assert_string_equal(flat_name, "AD"); + assert_null(site); + assert_string_equal(forest, "ad.devel"); + + talloc_free(flat_name); + talloc_free(site); + talloc_free(forest); + ret = netlogon_get_domain_info(test_ctx, attrs, true, &flat_name, &site, &forest); + assert_int_equal(ret, EOK); + assert_string_equal(flat_name, "AD"); + assert_null(site); + assert_string_equal(forest, "ad.devel"); + + talloc_free(flat_name); + talloc_free(site); + talloc_free(forest); + talloc_free(val.data); + talloc_free(attrs); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int ret; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_ad_create_1way_trust_options, + test_ad_common_setup, + test_ad_common_teardown), + cmocka_unit_test_setup_teardown(test_ad_create_2way_trust_options, + test_ad_common_setup, + test_ad_common_teardown), + cmocka_unit_test_setup_teardown(test_ad_get_dom_ldap_conn, + test_ldap_conn_setup, + test_ldap_conn_teardown), + cmocka_unit_test_setup_teardown(test_gc_conn_list, + test_ldap_conn_setup, + test_ldap_conn_teardown), + cmocka_unit_test_setup_teardown(test_ldap_conn_list, + test_ldap_conn_setup, + test_ldap_conn_teardown), + cmocka_unit_test_setup_teardown(test_user_conn_list, + test_ldap_conn_setup, + test_ldap_conn_teardown), + cmocka_unit_test_setup_teardown(test_check_if_pac_is_available, + test_ad_sysdb_setup, + test_ad_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_ad_get_data_from_pac, + test_ad_common_setup, + test_ad_common_teardown), + cmocka_unit_test_setup_teardown(test_ad_get_sids_from_pac, + test_ad_common_setup, + test_ad_common_teardown), +#ifdef HAVE_STRUCT_PAC_LOGON_INFO_RESOURCE_GROUPS + cmocka_unit_test_setup_teardown(test_ad_get_sids_from_pac_with_resource_groups, + test_ad_common_setup, + test_ad_common_teardown), +#endif +#ifdef HAVE_STRUCT_PAC_UPN_DNS_INFO_EX + cmocka_unit_test_setup_teardown(test_ad_pac_with_has_sam_name_and_sid, + test_ad_common_setup, + test_ad_common_teardown), + cmocka_unit_test_setup_teardown(test_ad_pac_missing_upn_dns_info, + test_ad_common_setup, + test_ad_common_teardown), +#endif + cmocka_unit_test_setup_teardown(test_ad_get_pac_data_from_user_entry, + test_ad_common_setup, + test_ad_common_teardown), + cmocka_unit_test_setup_teardown(test_netlogon_get_domain_info, + test_ad_common_setup, + test_ad_common_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + + ret = cmocka_run_group_tests(tests, NULL, NULL); + + return ret; +} diff --git a/src/tests/cmocka/test_ad_gpo.c b/src/tests/cmocka/test_ad_gpo.c new file mode 100644 index 0000000..d49f6c5 --- /dev/null +++ b/src/tests/cmocka/test_ad_gpo.c @@ -0,0 +1,496 @@ +/* + Authors: + Yassir Elley <yelley@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: GPO unit tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <unistd.h> +#include <sys/types.h> +#include <ifaddrs.h> +#include <arpa/inet.h> + +/* In order to access opaque types */ +#include "providers/ad/ad_gpo.c" + +#include "tests/cmocka/common_mock.h" + +struct ad_gpo_test_ctx { + struct ldb_context *ldb_ctx; +}; + +static struct ad_gpo_test_ctx *test_ctx; + +static int ad_gpo_test_setup(void **state) +{ + assert_true(leak_check_setup()); + test_ctx = talloc_zero(global_talloc_context, + struct ad_gpo_test_ctx); + assert_non_null(test_ctx); + + test_ctx->ldb_ctx = ldb_init(test_ctx, NULL); + assert_non_null(test_ctx->ldb_ctx); + return 0; +} + +static int ad_gpo_test_teardown(void **state) +{ + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +struct som_list_result { + const int result; + const int num_soms; + const char **som_dns; +}; + +/* + * Test parsing target DN into som components + */ +static void test_populate_som_list(const char *target_dn, + struct som_list_result *expected) +{ + errno_t ret; + int i; + int num_soms; + struct gp_som **som_list = NULL; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(global_talloc_context); + assert_non_null(tmp_ctx); + check_leaks_push(tmp_ctx); + + ret = ad_gpo_populate_som_list(tmp_ctx, + test_ctx->ldb_ctx, + target_dn, + &num_soms, + &som_list); + + assert_int_equal(ret, expected->result); + if (ret != EOK) { + goto done; + } + + assert_int_equal(num_soms, expected->num_soms); + + for (i=0; i<expected->num_soms; i++){ + bool equal = true; + if (strncmp(som_list[i]->som_dn, + expected->som_dns[i], + strlen(expected->som_dns[i])) != 0) { + equal = false; + } + + assert_int_equal(equal, true); + } + + if (som_list) { + talloc_free(som_list); + } + + done: + assert_true(check_leaks_pop(tmp_ctx) == true); + talloc_free(tmp_ctx); +} + +void test_populate_som_list_plain(void **state) +{ + const char *som_dns[] = {"OU=West OU,OU=Sales OU,DC=foo,DC=com", + "OU=Sales OU,DC=foo,DC=com", + "DC=foo,DC=com"}; + + struct som_list_result expected = { + .result = EOK, + .num_soms = 3, + .som_dns = som_dns + }; + + test_populate_som_list("CN=F21-Client,OU=West OU,OU=Sales OU,DC=foo,DC=com", + &expected); +} + +void test_populate_som_list_malformed(void **state) +{ + struct som_list_result expected = { + .result = EINVAL, + }; + + test_populate_som_list("malformed target dn", &expected); +} + +struct gplink_list_result { + const int result; + const int num_gplinks; + const char **gpo_dns; + bool *enforced; +}; + +/* + * Test parsing raw_gplink_value into gplink components + */ +static void test_populate_gplink_list(const char *input_gplink_value, + bool allow_enforced_only, + struct gplink_list_result *expected) +{ + errno_t ret; + int i; + struct gp_gplink **gplink_list = NULL; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(global_talloc_context); + assert_non_null(tmp_ctx); + check_leaks_push(tmp_ctx); + + char *raw_gplink_value = talloc_strdup(tmp_ctx, input_gplink_value); + + ret = ad_gpo_populate_gplink_list(tmp_ctx, + NULL, + raw_gplink_value, + &gplink_list, + allow_enforced_only); + + talloc_free(raw_gplink_value); + + assert_int_equal(ret, expected->result); + if (ret != EOK) { + goto done; + } + + for (i=0; i<expected->num_gplinks; i++){ + bool equal = true; + if (strncmp(gplink_list[i]->gpo_dn, + expected->gpo_dns[i], + strlen(expected->gpo_dns[i])) != 0) { + equal = false; + } + + if (gplink_list[i]->enforced != expected->enforced[i]) + equal = false; + + assert_int_equal(equal, true); + } + + if (gplink_list) { + talloc_free(gplink_list); + } + + done: + assert_true(check_leaks_pop(tmp_ctx) == true); + talloc_free(tmp_ctx); +} + +void test_populate_gplink_list_plain(void **state) +{ + const char *gpo_dns[] = {"OU=Sales,DC=FOO,DC=COM", "DC=FOO,DC=COM"}; + bool enforced[] = {false, true}; + + struct gplink_list_result expected = { + .result = EOK, + .num_gplinks = 2, + .gpo_dns = gpo_dns, + .enforced = enforced + }; + + test_populate_gplink_list("[OU=Sales,DC=FOO,DC=COM;0][DC=FOO,DC=COM;2]", + false, + &expected); +} + +void test_populate_gplink_list_with_ignored(void **state) +{ + const char *gpo_dns[] = {"OU=Sales,DC=FOO,DC=COM"}; + bool enforced[] = {false}; + + struct gplink_list_result expected = { + .result = EOK, + .num_gplinks = 1, + .gpo_dns = gpo_dns, + .enforced = enforced + }; + + test_populate_gplink_list("[OU=Sales,DC=FOO,DC=COM;0][DC=ignored;1]", + false, + &expected); +} + +void test_populate_gplink_list_with_allow_enforced(void **state) +{ + const char *gpo_dns[] = {"DC=FOO,DC=COM"}; + bool enforced[] = {true}; + + struct gplink_list_result expected = { + .result = EOK, + .num_gplinks = 1, + .gpo_dns = gpo_dns, + .enforced = enforced + }; + + test_populate_gplink_list("[OU=Sales,DC=FOO,DC=COM;0][DC=FOO,DC=COM;2]", + true, + &expected); +} + +void test_populate_gplink_list_malformed(void **state) +{ + struct gplink_list_result expected = { + .result = EINVAL, + }; + + test_populate_gplink_list(NULL, false, &expected); + test_populate_gplink_list("[malformed]", false, &expected); + /* the GPLinkOptions value (after semicolon) must be between 0 and 3 */ + test_populate_gplink_list("[gpo_dn; 4]", false, &expected); +} + +/* + * Test SID-matching logic + */ +static void test_ad_gpo_ace_includes_client_sid(const char *user_sid, + const char *host_sid, + const char **group_sids, + int group_size, + const char **host_group_sids, + int host_group_size, + struct dom_sid ace_dom_sid, + bool expected) +{ + errno_t ret; + enum idmap_error_code err; + struct sss_idmap_ctx *idmap_ctx; + bool includes_client_sid; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(global_talloc_context); + assert_non_null(tmp_ctx); + check_leaks_push(tmp_ctx); + + err = sss_idmap_init(sss_idmap_talloc, tmp_ctx, sss_idmap_talloc_free, + &idmap_ctx); + assert_int_equal(err, IDMAP_SUCCESS); + + ret = ad_gpo_ace_includes_client_sid(user_sid, host_sid, group_sids, + group_size, host_group_sids, + host_group_size, ace_dom_sid, + idmap_ctx, &includes_client_sid); + talloc_free(idmap_ctx); + + assert_int_equal(ret, EOK); + + assert_int_equal(includes_client_sid, expected); + + assert_true(check_leaks_pop(tmp_ctx) == true); + talloc_free(tmp_ctx); +} + +void test_ad_gpo_ace_includes_client_sid_true(void **state) +{ + /* ace_dom_sid represents "S-1-5-21-2-3-4" */ + struct dom_sid ace_dom_sid = {1, 4, {0, 0, 0, 0, 0, 5}, {21, 2, 3, 4}}; + + const char *user_sid = "S-1-5-21-1175337206-4250576914-2321192831-1103"; + const char *host_sid = "S-1-5-21-1898687337-2196588786-2775055786-2102"; + + int group_size = 2; + const char *group_sids[] = {"S-1-5-21-2-3-4", + "S-1-5-21-2-3-5"}; + + int host_group_size = 0; + const char *host_group_sids[] = { NULL }; + + test_ad_gpo_ace_includes_client_sid(user_sid, host_sid, group_sids, + group_size, host_group_sids, + host_group_size, ace_dom_sid, true); +} + +void test_ad_gpo_ace_includes_client_sid_false(void **state) +{ + /* ace_dom_sid represents "S-1-5-21-2-3-4" */ + struct dom_sid ace_dom_sid = {1, 4, {0, 0, 0, 0, 0, 5}, {21, 2, 3, 4}}; + + const char *user_sid = "S-1-5-21-1175337206-4250576914-2321192831-1103"; + const char *host_sid = "S-1-5-21-1898687337-2196588786-2775055786-2102"; + + int group_size = 2; + const char *group_sids[] = {"S-1-5-21-2-3-5", + "S-1-5-21-2-3-6"}; + + int host_group_size = 0; + const char *host_group_sids[] = { NULL }; + + test_ad_gpo_ace_includes_client_sid(user_sid, host_sid, group_sids, + group_size, host_group_sids, + host_group_size, ace_dom_sid, false); +} + +void test_ad_gpo_ace_includes_host_sid_true(void **state) +{ + /* ace_dom_sid represents "S-1-5-21-1898687337-2196588786-2775055786-2102" */ + struct dom_sid ace_dom_sid = {1, 5, {0, 0, 0, 0, 0, 5}, {21, 1898687337, 2196588786, 2775055786, 2102}}; + + const char *user_sid = "S-1-5-21-1175337206-4250576914-2321192831-1103"; + const char *host_sid = "S-1-5-21-1898687337-2196588786-2775055786-2102"; + + int group_size = 0; + const char *group_sids[] = {}; + + int host_group_size = 0; + const char *host_group_sids[] = { NULL }; + + test_ad_gpo_ace_includes_client_sid(user_sid, host_sid, group_sids, + group_size, host_group_sids, + host_group_size, ace_dom_sid, true); +} + +uint8_t test_sid_data[] = { +0x01, 0x00, 0x04, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x34, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, +0xbd, 0x00, 0x0e, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, +0xda, 0x0e, 0xba, 0x60, 0x0f, 0xa2, 0xf4, 0x55, 0xb5, 0x57, 0x47, 0xf8, 0x00, 0x02, 0x00, 0x00, +0x00, 0x0a, 0x24, 0x00, 0xff, 0x00, 0x0f, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, +0x15, 0x00, 0x00, 0x00, 0xda, 0x0e, 0xba, 0x60, 0x0f, 0xa2, 0xf4, 0x55, 0xb5, 0x57, 0x47, 0xf8, +0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0xbd, 0x00, 0x0e, 0x00, 0x01, 0x05, 0x00, 0x00, +0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0xda, 0x0e, 0xba, 0x60, 0x0f, 0xa2, 0xf4, 0x55, +0xb5, 0x57, 0x47, 0xf8, 0x07, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x24, 0x00, 0xff, 0x00, 0x0f, 0x00, +0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0xda, 0x0e, 0xba, 0x60, +0x0f, 0xa2, 0xf4, 0x55, 0xb5, 0x57, 0x47, 0xf8, 0x07, 0x02, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, +0xbd, 0x00, 0x0e, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, +0xda, 0x0e, 0xba, 0x60, 0x0f, 0xa2, 0xf4, 0x55, 0xb5, 0x57, 0x47, 0xf8, 0x00, 0x02, 0x00, 0x00, +0x00, 0x0a, 0x14, 0x00, 0xff, 0x00, 0x0f, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, +0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0xff, 0x00, 0x0f, 0x00, 0x01, 0x01, 0x00, 0x00, +0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x94, 0x00, 0x02, 0x00, +0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x02, 0x28, 0x00, +0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8f, 0xfd, 0xac, 0xed, 0xb3, 0xff, 0xd1, 0x11, +0xb4, 0x1d, 0x00, 0xa0, 0xc9, 0x68, 0xf9, 0x39, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, +0x0b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x94, 0x00, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, +0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x00, 0x00 +}; + +void test_ad_gpo_parse_sd(void **state) +{ + int ret; + struct security_descriptor *sd = NULL; + + ret = ad_gpo_parse_sd(test_ctx, NULL, 0, &sd); + assert_int_equal(ret, EINVAL); + + ret = ad_gpo_parse_sd(test_ctx, test_sid_data, sizeof(test_sid_data), &sd); + assert_int_equal(ret, EOK); + assert_non_null(sd); + assert_int_equal(sd->revision, 1); + assert_int_equal(sd->type, 39940); + assert_null(sd->owner_sid); + assert_null(sd->group_sid); + assert_null(sd->sacl); + assert_non_null(sd->dacl); + assert_int_equal(sd->dacl->revision, 4); + assert_int_equal(sd->dacl->size, 308); + assert_int_equal(sd->dacl->num_aces, 10); + assert_int_equal(sd->dacl->aces[0].type, 0); + assert_int_equal(sd->dacl->aces[0].flags, 0); + assert_int_equal(sd->dacl->aces[0].size, 36); + assert_int_equal(sd->dacl->aces[0].access_mask, 917693); + /* There are more components and ACEs in the security_descriptor struct + * which are not checked here. */ + + talloc_free(sd); +} + +errno_t ad_gpo_parse_ini_file(const char *smb_path, int *_gpt_version); + +void test_ad_gpo_parse_ini_file(void **state) +{ + int version = -1; + + ad_gpo_parse_ini_file(ABS_SRC_DIR"/src/tests/cmocka/GPT.INI", &version); + + assert_int_equal(version, 6); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_populate_som_list_plain, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_populate_som_list_malformed, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_populate_gplink_list_plain, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_populate_gplink_list_with_ignored, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_populate_gplink_list_with_allow_enforced, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_populate_gplink_list_malformed, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_ad_gpo_ace_includes_client_sid_true, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_ad_gpo_ace_includes_client_sid_false, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_ad_gpo_ace_includes_host_sid_true, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_ad_gpo_parse_sd, + ad_gpo_test_setup, + ad_gpo_test_teardown), + cmocka_unit_test_setup_teardown(test_ad_gpo_parse_ini_file, + ad_gpo_test_setup, + ad_gpo_test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_ad_subdomains.c b/src/tests/cmocka/test_ad_subdomains.c new file mode 100644 index 0000000..20c06aa --- /dev/null +++ b/src/tests/cmocka/test_ad_subdomains.c @@ -0,0 +1,328 @@ +/* + Authors: + Petr ト憩ch <pcech@redhat.com> + + Copyright (C) 2016 Red Hat + + SSSD tests: AD subdomain tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" +#include "providers/ad/ad_common.h" + +#include "providers/ad/ad_subdomains.c" +#include "providers/ad/ad_opts.c" + +#define AD_DOMAIN "ad_domain.domain.test" +#define DOMAIN_1 "one.domain.test" +#define DOMAIN_2 "two.domain.test" + +struct test_ad_subdom_ctx { + struct ad_id_ctx *ad_id_ctx; +}; + +static struct ad_id_ctx * +test_ad_subdom_init_ad_id_ctx(TALLOC_CTX *mem_ctx) +{ + struct ad_id_ctx *ad_id_ctx; + struct ad_options *ad_options; + errno_t ret; + + ad_id_ctx = talloc_zero(mem_ctx, struct ad_id_ctx); + assert_non_null(ad_id_ctx); + + ad_options = talloc_zero(ad_id_ctx, struct ad_options); + assert_non_null(ad_options); + + ret = dp_copy_defaults(ad_options, + ad_basic_opts, + AD_OPTS_BASIC, + &ad_options->basic); + assert_int_equal(ret, EOK); + + ad_id_ctx->ad_options = ad_options; + + return ad_id_ctx; +} + +static int test_ad_subdom_setup(void **state) +{ + struct test_ad_subdom_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ad_subdom_ctx); + assert_non_null(test_ctx); + + test_ctx->ad_id_ctx = NULL; + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int test_ad_subdom_teardown(void **state) +{ + struct test_ad_subdom_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx); + assert_non_null(test_ctx); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_ad_subdom_default(void **state) +{ + struct test_ad_subdom_ctx *test_ctx; + const char **ad_enabled_domains = NULL; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx); + test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx); + assert_non_null(test_ctx->ad_id_ctx); + + ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx, + AD_DOMAIN, + &ad_enabled_domains); + assert_int_equal(ret, EOK); + assert_null(ad_enabled_domains); + + talloc_zfree(test_ctx->ad_id_ctx); +} + +static void test_ad_subdom_add_one(void **state) +{ + struct test_ad_subdom_ctx *test_ctx; + const char **ad_enabled_domains = NULL; + int enabled_domains_count; + int domain_count = 2; + const char *domains[domain_count]; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx); + test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx); + assert_non_null(test_ctx->ad_id_ctx); + + ret = dp_opt_set_string(test_ctx->ad_id_ctx->ad_options->basic, + AD_ENABLED_DOMAINS, DOMAIN_1); + assert_int_equal(ret, EOK); + + ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx, + AD_DOMAIN, + &ad_enabled_domains); + assert_int_equal(ret, EOK); + assert_non_null(ad_enabled_domains); + + for (enabled_domains_count = 0; + ad_enabled_domains[enabled_domains_count] != NULL; + enabled_domains_count++) { + } + assert_int_equal(domain_count, enabled_domains_count); + + domains[0] = AD_DOMAIN; + domains[1] = DOMAIN_1; + assert_true(are_values_in_array(domains, domain_count, + ad_enabled_domains, enabled_domains_count)); + + talloc_zfree(test_ctx->ad_id_ctx); + talloc_zfree(ad_enabled_domains); +} + +static void test_ad_subdom_add_two(void **state) +{ + struct test_ad_subdom_ctx *test_ctx; + const char **ad_enabled_domains = NULL; + int enabled_domains_count; + int domain_count = 3; + const char *domains[domain_count]; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx); + test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx); + assert_non_null(test_ctx->ad_id_ctx); + + ret = dp_opt_set_string(test_ctx->ad_id_ctx->ad_options->basic, + AD_ENABLED_DOMAINS, DOMAIN_1","DOMAIN_2); + assert_int_equal(ret, EOK); + + ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx, + AD_DOMAIN, + &ad_enabled_domains); + assert_int_equal(ret, EOK); + assert_non_null(ad_enabled_domains); + + for (enabled_domains_count = 0; + ad_enabled_domains[enabled_domains_count] != NULL; + enabled_domains_count++) { + } + assert_int_equal(domain_count, enabled_domains_count); + + domains[0] = AD_DOMAIN; + domains[1] = DOMAIN_1; + domains[2] = DOMAIN_2; + assert_true(are_values_in_array(domains, domain_count, + ad_enabled_domains, enabled_domains_count)); + + talloc_zfree(test_ctx->ad_id_ctx); + talloc_zfree(ad_enabled_domains); +} + +static void test_ad_subdom_add_master(void **state) +{ + struct test_ad_subdom_ctx *test_ctx; + const char **ad_enabled_domains = NULL; + int enabled_domains_count; + int domain_count = 1; + const char *domains[domain_count]; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx); + test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx); + assert_non_null(test_ctx->ad_id_ctx); + + ret = dp_opt_set_string(test_ctx->ad_id_ctx->ad_options->basic, + AD_ENABLED_DOMAINS, AD_DOMAIN); + assert_int_equal(ret, EOK); + + ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx, + AD_DOMAIN, + &ad_enabled_domains); + assert_int_equal(ret, EOK); + assert_non_null(ad_enabled_domains); + + for (enabled_domains_count = 0; + ad_enabled_domains[enabled_domains_count] != NULL; + enabled_domains_count++) { + } + assert_int_equal(domain_count, enabled_domains_count); + + domains[0] = AD_DOMAIN; + assert_true(are_values_in_array(domains, domain_count, + ad_enabled_domains, enabled_domains_count)); + + talloc_zfree(test_ctx->ad_id_ctx); + talloc_zfree(ad_enabled_domains); +} + +static void test_ad_subdom_add_two_with_master(void **state) +{ + struct test_ad_subdom_ctx *test_ctx; + const char **ad_enabled_domains = NULL; + int enabled_domains_count; + int domain_count = 3; + const char *domains[domain_count]; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct test_ad_subdom_ctx); + test_ctx->ad_id_ctx = test_ad_subdom_init_ad_id_ctx(test_ctx); + assert_non_null(test_ctx->ad_id_ctx); + + ret = dp_opt_set_string(test_ctx->ad_id_ctx->ad_options->basic, + AD_ENABLED_DOMAINS, + DOMAIN_1","AD_DOMAIN","DOMAIN_2); + assert_int_equal(ret, EOK); + + ret = ad_get_enabled_domains(test_ctx, test_ctx->ad_id_ctx, + AD_DOMAIN, + &ad_enabled_domains); + assert_int_equal(ret, EOK); + assert_non_null(ad_enabled_domains); + + for (enabled_domains_count = 0; + ad_enabled_domains[enabled_domains_count] != NULL; + enabled_domains_count++) { + } + assert_int_equal(domain_count, enabled_domains_count); + + domains[0] = AD_DOMAIN; + domains[1] = DOMAIN_1; + domains[2] = DOMAIN_2; + assert_true(are_values_in_array(domains, domain_count, + ad_enabled_domains, enabled_domains_count)); + + talloc_zfree(test_ctx->ad_id_ctx); + talloc_zfree(ad_enabled_domains); +} + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_ad_subdom_default, + test_ad_subdom_setup, + test_ad_subdom_teardown), + cmocka_unit_test_setup_teardown(test_ad_subdom_add_one, + test_ad_subdom_setup, + test_ad_subdom_teardown), + cmocka_unit_test_setup_teardown(test_ad_subdom_add_two, + test_ad_subdom_setup, + test_ad_subdom_teardown), + cmocka_unit_test_setup_teardown(test_ad_subdom_add_master, + test_ad_subdom_setup, + test_ad_subdom_teardown), + cmocka_unit_test_setup_teardown(test_ad_subdom_add_two_with_master, + test_ad_subdom_setup, + test_ad_subdom_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + return rv; +} diff --git a/src/tests/cmocka/test_authtok.c b/src/tests/cmocka/test_authtok.c new file mode 100644 index 0000000..c736fd5 --- /dev/null +++ b/src/tests/cmocka/test_authtok.c @@ -0,0 +1,814 @@ +/* + SSSD + + authtok - Utilities tests + + Authors: + Pallavi Jha <pallavikumarijha@gmail.com> + + Copyright (C) 2013 Red Hat + + 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 <string.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" + +#include "util/authtok.h" + + +struct test_state { + struct sss_auth_token *authtoken; +}; + +static int setup(void **state) +{ + struct test_state *ts = NULL; + + assert_true(leak_check_setup()); + + ts = talloc(global_talloc_context, struct test_state); + assert_non_null(ts); + + ts->authtoken = sss_authtok_new(ts); + assert_non_null(ts->authtoken); + + check_leaks_push(ts); + *state = (void *)ts; + return 0; +} + +static int teardown(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + + assert_non_null(ts); + + assert_true(check_leaks_pop(ts)); + talloc_free(ts); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_sss_authtok_new(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + struct sss_auth_token *authtoken; + + authtoken = sss_authtok_new(ts); + assert_non_null(authtoken); + + talloc_free(authtoken); +} + +/* @test_authtok_type_x : tests following functions for different value of type + * sss_authtok_set + * sss_authtok_get_type + * sss_authtok_get_size + * sss_authtok_get_data + * sss_authtok_get_password + * sss_authtok_get_ccfile + * + * @test_authtok_type_password : type => SSS_AUTHTOK_TYPE_PASSWORD + * @test_authtok_type_ccfile : type => SSS_AUTHTOK_TYPE_CCFILE + * @test_authtok_type_empty : type => SSS_AUTHTOK_TYPE_EMPTY + */ + +/* Test when type has value SSS_AUTHTOK_TYPE_PASSWORD */ +static void test_sss_authtok_password(void **state) +{ + size_t len; + errno_t ret; + char *data; + size_t ret_len; + const char *pwd; + struct test_state *ts; + enum sss_authtok_type type; + + ts = talloc_get_type_abort(*state, struct test_state); + data = talloc_strdup(ts, "password"); + assert_non_null(data); + + len = strlen(data) + 1; + type = SSS_AUTHTOK_TYPE_PASSWORD; + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len); + + assert_int_equal(ret, EOK); + assert_int_equal(type, sss_authtok_get_type(ts->authtoken)); + assert_int_equal(len, sss_authtok_get_size(ts->authtoken)); + assert_string_equal(data, (char *)sss_authtok_get_data(ts->authtoken)); + + ret = sss_authtok_get_password(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); + + ret = sss_authtok_set_password(ts->authtoken, data, len); + assert_int_equal(ret, EOK); + + ret = sss_authtok_get_password(ts->authtoken, &pwd, &ret_len); + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); + + talloc_free(data); + sss_authtok_set_empty(ts->authtoken); +} + +/* Test when type has value SSS_AUTHTOK_TYPE_CCFILE */ +static void test_sss_authtok_ccfile(void **state) +{ + size_t len; + errno_t ret; + char *data; + size_t ret_len; + const char *pwd; + struct test_state *ts; + enum sss_authtok_type type; + + ts = talloc_get_type_abort(*state, struct test_state); + data = talloc_strdup(ts, "path/to/cc_file"); + assert_non_null(data); + + len = strlen(data) + 1; + type = SSS_AUTHTOK_TYPE_CCFILE; + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len); + + assert_int_equal(ret, EOK); + assert_int_equal(type, sss_authtok_get_type(ts->authtoken)); + assert_int_equal(len, sss_authtok_get_size(ts->authtoken)); + assert_string_equal(data, (char *)sss_authtok_get_data(ts->authtoken)); + + ret = sss_authtok_get_ccfile(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); + + ret = sss_authtok_set_ccfile(ts->authtoken, data, len); + + assert_int_equal(ret, EOK); + + ret = sss_authtok_get_ccfile(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); + + + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *) data, 0); + + assert_int_equal(ret, EOK); + assert_int_equal(type, sss_authtok_get_type(ts->authtoken)); + assert_int_equal(len, sss_authtok_get_size(ts->authtoken)); + assert_string_equal(data, (char *)sss_authtok_get_data(ts->authtoken)); + + ret = sss_authtok_get_ccfile(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); + + talloc_free(data); + sss_authtok_set_empty(ts->authtoken); +} + +/* Test when type has value SSS_AUTHTOK_TYPE_EMPTY */ +static void test_sss_authtok_empty(void **state) +{ + errno_t ret; + size_t ret_len; + const char *pwd; + struct test_state *ts; + enum sss_authtok_type type; + + type = SSS_AUTHTOK_TYPE_EMPTY; + ts = talloc_get_type_abort(*state, struct test_state); + ret = sss_authtok_set(ts->authtoken, type, NULL, 0); + + assert_int_equal(ret, EOK); + assert_int_equal(type, sss_authtok_get_type(ts->authtoken)); + assert_int_equal(0, sss_authtok_get_size(ts->authtoken)); + assert_null(sss_authtok_get_data(ts->authtoken)); + + ret = sss_authtok_get_password(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, ENOENT); + + ret = sss_authtok_get_ccfile(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, ENOENT); + + sss_authtok_set_empty(ts->authtoken); + + assert_int_equal(type, sss_authtok_get_type(ts->authtoken)); + assert_int_equal(0, sss_authtok_get_size(ts->authtoken)); + assert_null(sss_authtok_get_data(ts->authtoken)); + + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t*)"", 0); + assert_int_equal(ret, EOK); + + assert_int_equal(type, sss_authtok_get_type(ts->authtoken)); + assert_int_equal(EOK, sss_authtok_get_size(ts->authtoken)); + assert_null(sss_authtok_get_data(ts->authtoken)); + + ret = sss_authtok_get_password(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, ENOENT); + + ret = sss_authtok_get_ccfile(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, ENOENT); +} + +static void test_sss_authtok_wipe_password(void **state) +{ + size_t len; + errno_t ret; + char *data; + size_t ret_len; + const char *pwd; + struct test_state *ts; + enum sss_authtok_type type; + + ts = talloc_get_type_abort(*state, struct test_state); + data = talloc_strdup(ts, "password"); + assert_non_null(data); + + len = strlen(data) + 1; + type = SSS_AUTHTOK_TYPE_PASSWORD; + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len); + + assert_int_equal(ret, EOK); + + sss_authtok_wipe_password(ts->authtoken); + + ret = sss_authtok_get_password(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, EOK); + assert_string_equal(pwd, ""); + assert_int_equal(len - 1, ret_len); + + sss_authtok_set_empty(ts->authtoken); + talloc_free(data); +} + +static void test_sss_authtok_copy(void **state) +{ + size_t len; + errno_t ret; + char *data; + struct test_state *ts; + enum sss_authtok_type type; + struct sss_auth_token *dest_authtoken; + + ts= talloc_get_type_abort(*state, struct test_state); + + dest_authtoken = sss_authtok_new(ts); + assert_non_null(dest_authtoken); + + data = talloc_strdup(ts, "password"); + assert_non_null(data); + + len = strlen(data) + 1; + type = SSS_AUTHTOK_TYPE_EMPTY; + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len); + + assert_int_equal(ret, EOK); + assert_int_equal(EOK, sss_authtok_copy(ts->authtoken, dest_authtoken)); + assert_int_equal(type, sss_authtok_get_type(dest_authtoken)); + + sss_authtok_set_empty(dest_authtoken); + type = SSS_AUTHTOK_TYPE_PASSWORD; + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len); + + assert_int_equal(ret, EOK); + + ret = sss_authtok_copy(ts->authtoken, dest_authtoken); + + assert_int_equal(ret, EOK); + assert_int_equal(type, sss_authtok_get_type(dest_authtoken)); + assert_string_equal(data, (char *)sss_authtok_get_data(dest_authtoken)); + assert_int_equal(len, sss_authtok_get_size(dest_authtoken)); + + sss_authtok_set_empty(dest_authtoken); + talloc_free(dest_authtoken); + sss_authtok_set_empty(ts->authtoken); + talloc_free(data); +} + +void test_sss_authtok_2fa(void **state) +{ + int ret; + const char *fa1; + size_t fa1_size; + const char *fa2; + size_t fa2_size; + struct test_state *ts; + + ts = talloc_get_type_abort(*state, struct test_state); + + ret = sss_authtok_set_2fa(NULL, "a", 0, "b", 0); + assert_int_equal(ret, EINVAL); + + /* Test missing first factor */ + ret = sss_authtok_set_2fa(ts->authtoken, NULL, 1, "b", 1); + assert_int_equal(ret, EINVAL); + /* Test missing second factor */ + ret = sss_authtok_set_2fa(ts->authtoken, "a", 1, NULL, 1); + assert_int_equal(ret, EINVAL); + /* Test wrong first factor length */ + ret = sss_authtok_set_2fa(ts->authtoken, "ab", 1, "b", 1); + assert_int_equal(ret, EINVAL); + /* Test wrong second factor length */ + ret = sss_authtok_set_2fa(ts->authtoken, "a", 1, "bc", 1); + assert_int_equal(ret, EINVAL); + + ret = sss_authtok_set_2fa(ts->authtoken, "a", 1, "bc", 2); + assert_int_equal(ret, EOK); + assert_int_equal(sss_authtok_get_size(ts->authtoken), + 2 * sizeof(uint32_t) + 5); + assert_int_equal(sss_authtok_get_type(ts->authtoken), SSS_AUTHTOK_TYPE_2FA); +#if __BYTE_ORDER == __LITTLE_ENDIAN + assert_memory_equal(sss_authtok_get_data(ts->authtoken), + "\2\0\0\0\3\0\0\0a\0bc\0", + 2 * sizeof(uint32_t) + 5); +#else + assert_memory_equal(sss_authtok_get_data(ts->authtoken), + "\0\0\0\2\0\0\0\3a\0bc\0", + 2 * sizeof(uint32_t) + 5); +#endif + + ret = sss_authtok_get_2fa(ts->authtoken, &fa1, &fa1_size, &fa2, &fa2_size); + assert_int_equal(ret, EOK); + assert_int_equal(fa1_size, 1); + assert_string_equal(fa1, "a"); + assert_int_equal(fa2_size, 2); + assert_string_equal(fa2, "bc"); + + sss_authtok_set_empty(ts->authtoken); + + /* check return code of empty token */ + ret = sss_authtok_get_2fa(ts->authtoken, &fa1, &fa1_size, &fa2, &fa2_size); + assert_int_equal(ret, ENOENT); + + /* check return code for other token type */ + ret = sss_authtok_set_password(ts->authtoken, "abc", 0); + assert_int_equal(ret, EOK); + + ret = sss_authtok_get_2fa(ts->authtoken, &fa1, &fa1_size, &fa2, &fa2_size); + assert_int_equal(ret, EACCES); + + sss_authtok_set_empty(ts->authtoken); + + /* check return code for garbage */ + ret = sss_authtok_set(ts->authtoken, SSS_AUTHTOK_TYPE_2FA, + (const uint8_t *) "1111222233334444", 16); + assert_int_equal(ret, EINVAL); + + sss_authtok_set_empty(ts->authtoken); +} + +void test_sss_authtok_2fa_blobs(void **state) +{ + int ret; + struct test_state *ts; + size_t needed_size; + uint8_t *buf; + char *fa1; + size_t fa1_len; + char *fa2; + size_t fa2_len; + + ts = talloc_get_type_abort(*state, struct test_state); + + ret = sss_auth_pack_2fa_blob(NULL, 0, "defg", 0, NULL, 0, &needed_size); + assert_int_equal(ret, EINVAL); + + ret = sss_auth_pack_2fa_blob("abc", 0, NULL, 0, NULL, 0, &needed_size); + assert_int_equal(ret, EINVAL); + + ret = sss_auth_pack_2fa_blob("", 0, "defg", 0, NULL, 0, &needed_size); + assert_int_equal(ret, EINVAL); + + ret = sss_auth_pack_2fa_blob("abc", 0, "", 0, NULL, 0, &needed_size); + assert_int_equal(ret, EINVAL); + + ret = sss_auth_pack_2fa_blob("abc", 0, "defg", 0, NULL, 0, &needed_size); + assert_int_equal(ret, EAGAIN); + + buf = talloc_size(ts, needed_size); + assert_non_null(buf); + + ret = sss_auth_pack_2fa_blob("abc", 0, "defg", 0, buf, needed_size, + &needed_size); + assert_int_equal(ret, EOK); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + assert_memory_equal(buf, "\4\0\0\0\5\0\0\0abc\0defg\0", needed_size); +#else + assert_memory_equal(buf, "\0\0\0\4\0\0\0\5abc\0defg\0", needed_size); +#endif + + ret = sss_auth_unpack_2fa_blob(ts, buf, needed_size, &fa1, &fa1_len, &fa2, + &fa2_len); + assert_int_equal(ret, EOK); + assert_int_equal(fa1_len, 3); + assert_string_equal(fa1, "abc"); + assert_int_equal(fa2_len, 4); + assert_string_equal(fa2, "defg"); + + talloc_free(buf); + talloc_free(fa1); + talloc_free(fa2); +} + +void test_sss_authtok_sc_blobs(void **state) +{ + int ret; + struct test_state *ts; + size_t needed_size; + uint8_t *buf; + const char *pin; + size_t pin_len; + const char *token_name; + size_t token_name_len; + const char *module_name; + size_t module_name_len; + const char *key_id; + size_t key_id_len; + const char *label; + size_t label_len; + + ts = talloc_get_type_abort(*state, struct test_state); + + ret = sss_auth_pack_sc_blob("abc", 0, "defg", 0, "hijkl", 0, "mnopqr", 0, + "stuvw", 0, NULL, 0, &needed_size); + assert_int_equal(ret, EAGAIN); + + buf = talloc_size(ts, needed_size); + assert_non_null(buf); + + ret = sss_auth_pack_sc_blob("abc", 0, "defg", 0, "hijkl", 0, "mnopqr", 0, + "stuvw", 0, buf, needed_size, &needed_size); + assert_int_equal(ret, EOK); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + assert_memory_equal(buf, "\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0\6\0\0\0abc\0defg\0hijkl\0mnopqr\0stuvw\0", + needed_size); +#else + assert_memory_equal(buf, "\0\0\0\4\0\0\0\5\0\0\0\6\0\0\0\7\0\0\0\6abc\0defg\0hijkl\0mnopqr\0stuvw\0", + needed_size); +#endif + + pin = sss_auth_get_pin_from_sc_blob(buf, needed_size); + assert_non_null(pin); + assert_string_equal(pin, "abc"); + pin = NULL; + + ret = sss_authtok_set(ts->authtoken, SSS_AUTHTOK_TYPE_SC_PIN, buf, + needed_size); + assert_int_equal(ret, EOK); + + ret = sss_authtok_get_sc(ts->authtoken, &pin, &pin_len, + &token_name, &token_name_len, + &module_name, &module_name_len, + &key_id, &key_id_len, + &label, &label_len); + assert_int_equal(ret, EOK); + assert_int_equal(pin_len, 3); + assert_string_equal(pin, "abc"); + assert_int_equal(token_name_len, 4); + assert_string_equal(token_name, "defg"); + assert_int_equal(module_name_len, 5); + assert_string_equal(module_name, "hijkl"); + assert_int_equal(key_id_len, 6); + assert_string_equal(key_id, "mnopqr"); + assert_int_equal(label_len, 5); + assert_string_equal(label, "stuvw"); + + ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL, + &token_name, &token_name_len, + &module_name, &module_name_len, + &key_id, &key_id_len, + &label, &label_len); + assert_int_equal(ret, EOK); + assert_int_equal(token_name_len, 4); + assert_string_equal(token_name, "defg"); + assert_int_equal(module_name_len, 5); + assert_string_equal(module_name, "hijkl"); + assert_int_equal(key_id_len, 6); + assert_string_equal(key_id, "mnopqr"); + assert_int_equal(label_len, 5); + assert_string_equal(label, "stuvw"); + + ret = sss_authtok_get_sc(ts->authtoken, NULL, NULL, + &token_name, NULL, + &module_name, NULL, + &key_id, NULL, + &label, NULL); + assert_int_equal(ret, EOK); + assert_string_equal(token_name, "defg"); + assert_string_equal(module_name, "hijkl"); + assert_string_equal(key_id, "mnopqr"); + assert_string_equal(label, "stuvw"); + + sss_authtok_set_empty(ts->authtoken); + talloc_free(buf); +} + +#define MISSING_NULL_CHECK do { \ + assert_int_equal(ret, EOK); \ + assert_int_equal(fa1_len, 3); \ + assert_string_equal(fa1, "abc"); \ + assert_int_equal(fa2_len, 4); \ + assert_string_equal(fa2, "defg"); \ + \ + talloc_free(fa1); \ + talloc_free(fa2); \ +} while (0) + +void test_sss_authtok_2fa_blobs_missing_null(void **state) +{ + int ret; + struct test_state *ts; + char *fa1; + size_t fa1_len; + char *fa2; + size_t fa2_len; +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t b0[] = {0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 'a', 'b', 'c', 0x00, 'd', 'e', 'f', 'g', 0x00}; + uint8_t b1[] = {0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 0x00}; + uint8_t b2[] = {0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 'a', 'b', 'c', 0x00, 'd', 'e', 'f', 'g'}; + uint8_t b3[] = {0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 'a', 'b', 'c', 'd', 'e', 'f', 'g'}; +#else + uint8_t b0[] = {0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 'a', 'b', 'c', 0x00, 'd', 'e', 'f', 'g', 0x00}; + uint8_t b1[] = {0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 0x00}; + uint8_t b2[] = {0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 'a', 'b', 'c', 0x00, 'd', 'e', 'f', 'g'}; + uint8_t b3[] = {0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 'a', 'b', 'c', 'd', 'e', 'f', 'g'}; +#endif + + + ts = talloc_get_type_abort(*state, struct test_state); + + ret = sss_auth_unpack_2fa_blob(ts, b0, sizeof(b0), &fa1, &fa1_len, &fa2, + &fa2_len); + MISSING_NULL_CHECK; + + ret = sss_auth_unpack_2fa_blob(ts, b1, sizeof(b1), &fa1, &fa1_len, &fa2, + &fa2_len); + MISSING_NULL_CHECK; + + ret = sss_auth_unpack_2fa_blob(ts, b2, sizeof(b2), &fa1, &fa1_len, &fa2, + &fa2_len); + MISSING_NULL_CHECK; + + ret = sss_auth_unpack_2fa_blob(ts, b3, sizeof(b3), &fa1, &fa1_len, &fa2, + &fa2_len); + MISSING_NULL_CHECK; +} + +void test_sss_authtok_sc_keypad(void **state) +{ + struct test_state *ts; + + ts = talloc_get_type_abort(*state, struct test_state); + + sss_authtok_set_sc_keypad(NULL); + + sss_authtok_set_sc_keypad(ts->authtoken); + assert_int_equal(sss_authtok_get_type(ts->authtoken), + SSS_AUTHTOK_TYPE_SC_KEYPAD); + assert_int_equal(sss_authtok_get_size(ts->authtoken), 0); + assert_null(sss_authtok_get_data(ts->authtoken)); +} + +void test_sss_authtok_sc_pin(void **state) +{ + struct test_state *ts; + int ret; + size_t size; + const char *pin; + size_t len; + + ts = talloc_get_type_abort(*state, struct test_state); + + ret = sss_authtok_set_sc_pin(NULL, NULL, 0); + assert_int_equal(ret, EFAULT); + + ret = sss_authtok_set_sc_pin(ts->authtoken, NULL, 0); + assert_int_equal(ret, EINVAL); + + ret = sss_authtok_set_sc_pin(ts->authtoken, "12345678", 0); + assert_int_equal(ret, EOK); + assert_int_equal(sss_authtok_get_type(ts->authtoken), + SSS_AUTHTOK_TYPE_SC_PIN); + size = sss_authtok_get_size(ts->authtoken); + assert_int_equal(size, 33); +#if __BYTE_ORDER == __LITTLE_ENDIAN + assert_memory_equal(sss_authtok_get_data(ts->authtoken), + "\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345678\0\0\0\0\0", + size); +#else + assert_memory_equal(sss_authtok_get_data(ts->authtoken), + "\0\0\0\11\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1" "12345678\0\0\0\0\0", + size); +#endif + + ret = sss_authtok_set_sc_pin(ts->authtoken, "12345678", 5); + assert_int_equal(ret, EOK); + assert_int_equal(sss_authtok_get_type(ts->authtoken), + SSS_AUTHTOK_TYPE_SC_PIN); + size = sss_authtok_get_size(ts->authtoken); + assert_int_equal(size, 30); +#if __BYTE_ORDER == __LITTLE_ENDIAN + assert_memory_equal(sss_authtok_get_data(ts->authtoken), + "\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0" "12345\0\0\0\0\0", + size); +#else + assert_memory_equal(sss_authtok_get_data(ts->authtoken), + "\0\0\0\6\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1" "12345\0\0\0\0\0", + size); +#endif + + ret = sss_authtok_get_sc_pin(ts->authtoken, &pin, &len); + assert_int_equal(ret, EOK); + assert_int_equal(len, 5); + assert_string_equal(pin, "12345"); + + sss_authtok_set_empty(ts->authtoken); + + ret = sss_authtok_get_sc_pin(ts->authtoken, &pin, &len); + assert_int_equal(ret, ENOENT); + + ret = sss_authtok_set_password(ts->authtoken, "12345", 0); + assert_int_equal(ret, EOK); + + ret = sss_authtok_get_sc_pin(ts->authtoken, &pin, &len); + assert_int_equal(ret, EACCES); + + sss_authtok_set_empty(ts->authtoken); + + ret = sss_authtok_get_sc_pin(NULL, &pin, &len); + assert_int_equal(ret, EFAULT); +} + +/* Test when type has value SSS_AUTHTOK_TYPE_2FA_SINGLE */ +static void test_sss_authtok_2fa_single(void **state) +{ + size_t len; + errno_t ret; + char *data; + size_t ret_len; + const char *pwd; + struct test_state *ts; + enum sss_authtok_type type; + + ts = talloc_get_type_abort(*state, struct test_state); + data = talloc_strdup(ts, "1stfacto2ndfactor"); + assert_non_null(data); + + len = strlen(data) + 1; + type = SSS_AUTHTOK_TYPE_2FA_SINGLE; + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len); + + assert_int_equal(ret, EOK); + assert_int_equal(type, sss_authtok_get_type(ts->authtoken)); + assert_int_equal(len, sss_authtok_get_size(ts->authtoken)); + assert_string_equal(data, (char *)sss_authtok_get_data(ts->authtoken)); + + ret = sss_authtok_get_2fa_single(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); + + ret = sss_authtok_set_2fa_single(ts->authtoken, data, len); + assert_int_equal(ret, EOK); + + ret = sss_authtok_get_2fa_single(ts->authtoken, &pwd, &ret_len); + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); + + talloc_free(data); + sss_authtok_set_empty(ts->authtoken); +} + +/* Test when type has value SSS_AUTHTOK_TYPE_OAUTH2 */ +static void test_sss_authtok_oauth2(void **state) +{ + size_t len; + errno_t ret; + char *data; + size_t ret_len; + const char *pwd; + struct test_state *ts; + enum sss_authtok_type type; + + ts = talloc_get_type_abort(*state, struct test_state); + data = talloc_strdup(ts, "one-time-password"); + assert_non_null(data); + + len = strlen(data) + 1; + type = SSS_AUTHTOK_TYPE_OAUTH2; + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len); + + assert_int_equal(ret, EOK); + assert_int_equal(type, sss_authtok_get_type(ts->authtoken)); + assert_int_equal(len, sss_authtok_get_size(ts->authtoken)); + assert_string_equal(data, (char *)sss_authtok_get_data(ts->authtoken)); + + ret = sss_authtok_get_oauth2(ts->authtoken, &pwd, &ret_len); + + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); + + ret = sss_authtok_set_oauth2(ts->authtoken, data, len); + assert_int_equal(ret, EOK); + + ret = sss_authtok_get_oauth2(ts->authtoken, &pwd, &ret_len); + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); + + talloc_free(data); + sss_authtok_set_empty(ts->authtoken); +} + + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sss_authtok_new, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_password, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_ccfile, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_empty, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_wipe_password, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_copy, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_2fa, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_2fa_blobs, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_2fa_blobs_missing_null, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_sc_keypad, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_sc_pin, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_sc_blobs, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_2fa_single, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_oauth2, + setup, teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_be_ptask.c b/src/tests/cmocka/test_be_ptask.c new file mode 100644 index 0000000..0bf262d --- /dev/null +++ b/src/tests/cmocka/test_be_ptask.c @@ -0,0 +1,1205 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <time.h> + +#include "providers/backend.h" +#include "providers/be_ptask_private.h" +#include "providers/be_ptask.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_be.h" +#include "tests/common.h" + +#define DELAY 2 +#define PERIOD 1 +#define TIMEOUT 123 + +#define new_test(test) \ + cmocka_unit_test_setup_teardown(test_ ## test, test_setup, test_teardown) + +struct test_ctx { + struct sss_test_ctx *tctx; + struct be_ctx *be_ctx; + + time_t when; + bool done; + + bool add_online_cb_called; + bool add_offline_cb_called; +}; + +#define mark_online(test_ctx) do { \ + test_ctx->be_ctx->offline = false; \ +} while (0) + +#define mark_offline(test_ctx) do { \ + test_ctx->be_ctx->offline = true; \ +} while (0) + +/* Since both test_ctx->done and ptask->req is marked as finished already + * in the sync _send function before a new execution is scheduled we need to + * rely on the fact that ptask->req is set to zero when a new timer is + * created. This way we guarantee that the condition is true only when + * the ptask is executed and a new one is scheduled. */ +#define is_sync_ptask_finished(test_ctx, ptask) \ + (test_ctx->done && ptask->req == NULL) + +static time_t get_current_time(void) +{ + struct timeval tv; + int ret; + + ret = gettimeofday(&tv, NULL); + assert_int_equal(0, ret); + return tv.tv_sec; +} + +/* Mock few backend functions so we don't have to bring the whole + * data provider into this test. */ + +bool be_is_offline(struct be_ctx *ctx) +{ + return ctx->offline; +} + +int be_add_online_cb(TALLOC_CTX *mem_ctx, + struct be_ctx *ctx, + be_callback_t cb, + void *pvt, + struct be_cb **online_cb) +{ + struct test_ctx *test_ctx = NULL; + + test_ctx = sss_mock_ptr_type(struct test_ctx *); + test_ctx->add_online_cb_called = true; + + return ERR_OK; +} + +int be_add_offline_cb(TALLOC_CTX *mem_ctx, + struct be_ctx *ctx, + be_callback_t cb, + void *pvt, + struct be_cb **offline_cb) +{ + struct test_ctx *test_ctx = NULL; + + test_ctx = sss_mock_ptr_type(struct test_ctx *); + test_ctx->add_offline_cb_called = true; + + return ERR_OK; +} + +struct test_be_ptask_state { + struct test_ctx *test_ctx; +}; + +struct tevent_req * test_be_ptask_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt) +{ + struct test_be_ptask_state *state = NULL; + struct test_ctx *test_ctx = NULL; + struct tevent_req *req = NULL; + + assert_non_null(ev); + assert_non_null(be_ctx); + assert_non_null(be_ptask); + assert_non_null(pvt); + + test_ctx = talloc_get_type(pvt, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->when = get_current_time(); + + req = tevent_req_create(mem_ctx, &state, struct test_be_ptask_state); + assert_non_null(req); + + state->test_ctx = test_ctx; + + tevent_req_done(req); + tevent_req_post(req, ev); + return req; +} + +struct tevent_req * test_be_ptask_null_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt) +{ + struct test_ctx *test_ctx = NULL; + assert_non_null(ev); + assert_non_null(be_ctx); + assert_non_null(be_ptask); + assert_non_null(pvt); + + test_ctx = talloc_get_type(pvt, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->when = get_current_time(); + test_ctx->done = true; + + return NULL; +} + +struct tevent_req * test_be_ptask_timeout_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt) +{ + struct test_be_ptask_state *state = NULL; + struct test_ctx *test_ctx = NULL; + struct tevent_req *req = NULL; + + assert_non_null(ev); + assert_non_null(be_ctx); + assert_non_null(be_ptask); + assert_non_null(pvt); + + test_ctx = talloc_get_type(pvt, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->when = get_current_time(); + + req = tevent_req_create(mem_ctx, &state, struct test_be_ptask_state); + assert_non_null(req); + + state->test_ctx = test_ctx; + + /* we won't finish the request */ + + return req; +} + +errno_t test_be_ptask_recv(struct tevent_req *req) +{ + struct test_be_ptask_state *state = NULL; + + state = tevent_req_data(req, struct test_be_ptask_state); + assert_non_null(state); + + state->test_ctx->done = true; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return ERR_OK; +} + +errno_t test_be_ptask_error_recv(struct tevent_req *req) +{ + struct test_be_ptask_state *state = NULL; + + state = tevent_req_data(req, struct test_be_ptask_state); + assert_non_null(state); + + state->test_ctx->done = true; + + return ERR_INTERNAL; +} + +errno_t test_be_ptask_sync(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt) +{ + struct test_ctx *test_ctx = NULL; + + assert_non_null(ev); + assert_non_null(be_ctx); + assert_non_null(be_ptask); + assert_non_null(pvt); + + test_ctx = talloc_get_type(pvt, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->when = get_current_time(); + test_ctx->done = true; + + return ERR_OK; +} + +errno_t test_be_ptask_sync_error(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt) +{ + struct test_ctx *test_ctx = NULL; + + assert_non_null(ev); + assert_non_null(be_ctx); + assert_non_null(be_ptask); + assert_non_null(pvt); + + test_ctx = talloc_get_type(pvt, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->when = get_current_time(); + test_ctx->done = true; + + return ERR_INTERNAL; +} + +static int test_setup(void **state) +{ + struct test_ctx *test_ctx = NULL; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->tctx = create_ev_test_ctx(test_ctx); + assert_non_null(test_ctx->tctx); + + test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx); + assert_non_null(test_ctx->be_ctx); + + test_ctx->be_ctx->ev = tevent_context_init(test_ctx->be_ctx); + assert_non_null(test_ctx->be_ctx->ev); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int test_teardown(void **state) +{ + assert_true(check_leaks_pop(*state)); + talloc_zfree(*state); + assert_true(leak_check_teardown()); + return 0; +} + +void test_be_ptask_create_einval_be(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, NULL, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, NULL, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, EINVAL); + assert_null(ptask); +} + +void test_be_ptask_create_einval_period(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, 0, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, NULL, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, EINVAL); + assert_null(ptask); +} + +void test_be_ptask_create_einval_send(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, NULL, + test_be_ptask_recv, NULL, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, EINVAL); + assert_null(ptask); +} + +void test_be_ptask_create_einval_recv(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + NULL, NULL, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, EINVAL); + assert_null(ptask); +} + +void test_be_ptask_create_einval_name(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, NULL, NULL, + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, EINVAL); + assert_null(ptask); +} + +void test_be_ptask_mixed_from_flags_einval(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, NULL, "Test ptask", + BE_PTASK_OFFLINE_SKIP | + BE_PTASK_SCHEDULE_FROM_LAST | + BE_PTASK_SCHEDULE_FROM_NOW, + &ptask); + assert_int_equal(ret, EINVAL); + assert_null(ptask); +} + +void test_be_ptask_no_from_flags_einval(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, NULL, "Test ptask", + BE_PTASK_OFFLINE_SKIP, + &ptask); + assert_int_equal(ret, EINVAL); + assert_null(ptask); +} +void test_be_ptask_mixed_offline_flags_einval(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, NULL, "Test ptask", + BE_PTASK_OFFLINE_SKIP | + BE_PTASK_OFFLINE_DISABLE | + BE_PTASK_SCHEDULE_FROM_NOW, + &ptask); + assert_int_equal(ret, EINVAL); + assert_null(ptask); +} +void test_be_ptask_no_offline_flags_einval(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, NULL, "Test ptask", + BE_PTASK_SCHEDULE_FROM_NOW, + &ptask); + assert_int_equal(ret, EINVAL); + assert_null(ptask); +} + +void test_be_ptask_create_no_delay(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now; + errno_t ret; + + now = get_current_time(); + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now <= ptask->last_execution); + assert_true(now <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_create_first_delay(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now; + errno_t ret; + + now = get_current_time(); + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, DELAY, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now + DELAY <= ptask->last_execution); + assert_true(now + DELAY <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_disable(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + be_ptask_disable(ptask); + + assert_null(ptask->timer); + assert_false(ptask->enabled); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_enable(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now; + errno_t ret; + + now = get_current_time(); + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + be_ptask_disable(ptask); + + now = get_current_time(); + be_ptask_enable(ptask); + assert_non_null(ptask->timer); + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now <= ptask->last_execution); + assert_true(now <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_enable_delay(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, DELAY, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + be_ptask_disable(ptask); + test_ctx->done = false; + now = get_current_time(); + be_ptask_enable(ptask); + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now + DELAY <= ptask->last_execution); + assert_true(now + DELAY <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_postpone(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now; + errno_t ret; + + now = get_current_time(); + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, 30, 10, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + assert_true(now + 10 <= ptask->next_execution); + assert_true(now + 30 > ptask->next_execution); + + be_ptask_postpone(ptask); + assert_true(now + 30 <= ptask->next_execution); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_offline_skip(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t next_execution; + time_t now; + errno_t ret; + + mark_offline(test_ctx); + + now = get_current_time(); + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + next_execution = ptask->next_execution; + assert_true(now <= next_execution); + + while (ptask->next_execution == next_execution && !test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(next_execution + PERIOD <= ptask->next_execution); + assert_true(ptask->enabled); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_offline_disable(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + mark_offline(test_ctx); + + will_return(be_add_online_cb, test_ctx); + will_return(be_add_offline_cb, test_ctx); + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_DISABLE | + BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + assert_true(test_ctx->add_online_cb_called); + assert_true(test_ctx->add_offline_cb_called); + + while (ptask->enabled && !test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_false(ptask->enabled); + assert_false(test_ctx->done); + assert_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_offline_execute(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + mark_offline(test_ctx); + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_EXECUTE | + BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(ptask->enabled); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_reschedule_ok(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t next_execution; + time_t now; + errno_t ret; + + now = get_current_time(); + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + next_execution = ptask->next_execution; + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now <= ptask->last_execution); + assert_true(now <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + assert_true(next_execution + PERIOD <= ptask->next_execution); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_reschedule_null(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now = 0; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_null_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + while (!test_ctx->done) { + now = get_current_time(); + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now + PERIOD <= ptask->next_execution); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_reschedule_error(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now = 0; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_error_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + while (!test_ctx->done) { + now = get_current_time(); + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now + PERIOD <= ptask->next_execution); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_reschedule_timeout(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now = 0; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 1, + 0, test_be_ptask_timeout_send, + test_be_ptask_error_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + /* first iterate until the task is executed */ + while (!test_ctx->done && ptask->req == NULL) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + /* then iterate until the request is destroyed */ + while (!test_ctx->done && ptask->req != NULL) { + now = get_current_time(); + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_false(test_ctx->done); + assert_true(now + PERIOD <= ptask->next_execution); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_reschedule_backoff(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t next_execution; + time_t now_first; + time_t now_backoff = 0; + errno_t ret; + + now_first = get_current_time(); + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + PERIOD*2, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + /* first run */ + next_execution = ptask->next_execution; + + while (!test_ctx->done) { + /* We need to acquire timestamp for the second test here, since this + * is the closest value to the timestamp when the next event is + * scheduled. */ + now_backoff = get_current_time(); + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now_first <= ptask->last_execution); + assert_true(now_first <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + assert_true(next_execution + PERIOD <= ptask->next_execution); + assert_int_equal(PERIOD*2, ptask->period); + assert_non_null(ptask->timer); + + test_ctx->done = false; + + /* second run */ + next_execution = ptask->next_execution; + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now_backoff + PERIOD <= ptask->last_execution); + assert_true(now_backoff + PERIOD <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + assert_true(next_execution + PERIOD*2 <= ptask->next_execution); + assert_int_equal(PERIOD*2, ptask->period); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_get_period(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t out_period; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + + out_period = be_ptask_get_period(ptask); + assert_true(PERIOD == out_period); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_get_timeout(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t out_timeout; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, TIMEOUT, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP | BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + + out_timeout = be_ptask_get_timeout(ptask); + assert_true(TIMEOUT == out_timeout); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_running(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + mark_offline(test_ctx); + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_EXECUTE | + BE_PTASK_SCHEDULE_FROM_NOW, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + assert_true(ptask->enabled); + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + if (ptask->req != NULL) { + break; + } + } + + assert_true(be_ptask_running(ptask)); + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_no_periodic(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + errno_t ret; + + ret = be_ptask_create(test_ctx, test_ctx->be_ctx, 0, 0, DELAY, 0, 0, + 0, test_be_ptask_send, + test_be_ptask_recv, test_ctx, "Test ptask", + BE_PTASK_NO_PERIODIC | + BE_PTASK_OFFLINE_SKIP | + BE_PTASK_SCHEDULE_FROM_LAST, + &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_create_sync(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now; + errno_t ret; + + now = get_current_time(); + ret = be_ptask_create_sync(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_sync, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP, &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + while (!test_ctx->done) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now <= ptask->last_execution); + assert_true(now <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_sync_reschedule_ok(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t next_execution; + time_t now; + errno_t ret; + + now = get_current_time(); + ret = be_ptask_create_sync(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_sync, test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP, &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + next_execution = ptask->next_execution; + + while (!is_sync_ptask_finished(test_ctx, ptask)) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now <= ptask->last_execution); + assert_true(now <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + assert_true(next_execution + PERIOD <= ptask->next_execution); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_sync_reschedule_error(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t now = 0; + errno_t ret; + + ret = be_ptask_create_sync(test_ctx, test_ctx->be_ctx, PERIOD, 0, 0, 0, 0, + 0, test_be_ptask_sync_error, + test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP, &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + while (!is_sync_ptask_finished(test_ctx, ptask)) { + now = get_current_time(); + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now + PERIOD <= ptask->next_execution); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +void test_be_ptask_sync_reschedule_backoff(void **state) +{ + struct test_ctx *test_ctx = (struct test_ctx *)(*state); + struct be_ptask *ptask = NULL; + time_t next_execution; + time_t now_first; + time_t now_backoff = 0; + errno_t ret; + + now_first = get_current_time(); + ret = be_ptask_create_sync(test_ctx, test_ctx->be_ctx, PERIOD, + 0, 0, 0, 0, PERIOD*2, + test_be_ptask_sync_error, + test_ctx, "Test ptask", + BE_PTASK_OFFLINE_SKIP, &ptask); + assert_int_equal(ret, ERR_OK); + assert_non_null(ptask); + assert_non_null(ptask->timer); + + /* first run */ + next_execution = ptask->next_execution; + + while (!is_sync_ptask_finished(test_ctx, ptask)) { + /* We need to acquire timestamp for the second test here, since this + * is the closest value to the timestamp when the next event is + * scheduled. */ + now_backoff = get_current_time(); + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now_first <= ptask->last_execution); + assert_true(now_first <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + assert_true(next_execution + PERIOD <= ptask->next_execution); + assert_int_equal(PERIOD*2, ptask->period); + assert_non_null(ptask->timer); + + test_ctx->done = false; + + /* second run */ + next_execution = ptask->next_execution; + + while (!is_sync_ptask_finished(test_ctx, ptask)) { + tevent_loop_once(test_ctx->be_ctx->ev); + } + + assert_true(now_backoff + PERIOD <= ptask->last_execution); + assert_true(now_backoff + PERIOD <= test_ctx->when); + assert_true(ptask->last_execution <= test_ctx->when); + + assert_true(next_execution + PERIOD*2 <= ptask->next_execution); + assert_int_equal(PERIOD*2, ptask->period); + assert_non_null(ptask->timer); + + be_ptask_destroy(&ptask); + assert_null(ptask); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + new_test(be_ptask_create_einval_be), + new_test(be_ptask_create_einval_period), + new_test(be_ptask_create_einval_send), + new_test(be_ptask_create_einval_recv), + new_test(be_ptask_create_einval_name), + new_test(be_ptask_mixed_from_flags_einval), + new_test(be_ptask_no_from_flags_einval), + new_test(be_ptask_mixed_offline_flags_einval), + new_test(be_ptask_no_offline_flags_einval), + new_test(be_ptask_create_no_delay), + new_test(be_ptask_create_first_delay), + new_test(be_ptask_disable), + new_test(be_ptask_enable), + new_test(be_ptask_enable_delay), + new_test(be_ptask_postpone), + new_test(be_ptask_offline_skip), + new_test(be_ptask_offline_disable), + new_test(be_ptask_offline_execute), + new_test(be_ptask_reschedule_ok), + new_test(be_ptask_reschedule_null), + new_test(be_ptask_reschedule_error), + new_test(be_ptask_reschedule_timeout), + new_test(be_ptask_reschedule_backoff), + new_test(be_ptask_get_period), + new_test(be_ptask_get_timeout), + new_test(be_ptask_running), + new_test(be_ptask_no_periodic), + new_test(be_ptask_create_sync), + new_test(be_ptask_sync_reschedule_ok), + new_test(be_ptask_sync_reschedule_error), + new_test(be_ptask_sync_reschedule_backoff) + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_cert_utils.c b/src/tests/cmocka/test_cert_utils.c new file mode 100644 index 0000000..dff2336 --- /dev/null +++ b/src/tests/cmocka/test_cert_utils.c @@ -0,0 +1,892 @@ +/* + SSSD + + Certificates - Utilities tests + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2015 Red Hat + + 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 "config.h" + +#include <popt.h> +#include <tevent.h> +#include <openssl/objects.h> +#include <openssl/crypto.h> + +#include "util/cert.h" +#include "tests/cmocka/common_mock.h" +#include "util/crypto/sss_crypto.h" +#include "responder/ssh/ssh_private.h" + +#ifdef HAVE_TEST_CA +#include "tests/test_CA/SSSD_test_cert_pubsshkey_0001.h" +#include "tests/test_CA/SSSD_test_cert_x509_0001.h" +#include "tests/test_CA/SSSD_test_cert_pubsshkey_0002.h" +#include "tests/test_CA/SSSD_test_cert_x509_0002.h" +#include "tests/test_CA/SSSD_test_cert_pubsshkey_0007.h" +#include "tests/test_CA/SSSD_test_cert_x509_0007.h" +#include "tests/test_ECC_CA/SSSD_test_ECC_cert_pubsshkey_0001.h" +#include "tests/test_ECC_CA/SSSD_test_ECC_cert_x509_0001.h" +#else +#define SSSD_TEST_CERT_0001 "" +#define SSSD_TEST_CERT_SSH_KEY_0001 "" +#define SSSD_TEST_CERT_0002 "" +#define SSSD_TEST_CERT_SSH_KEY_0002 "" +#define SSSD_TEST_CERT_0007 "" +#define SSSD_TEST_CERT_SSH_KEY_0007 "" +#define SSSD_TEST_ECC_CERT_0001 "" +#define SSSD_TEST_ECC_CERT_SSH_KEY_0001 "" +#endif + +/* When run under valgrind with --trace-children=yes we have to increase the + * timeout not because p11_child needs much more time under valgrind but + * because of the way valgrind handles the children. */ +#define P11_CHILD_TIMEOUT 80 + +/* TODO: create a certificate for this test */ +const uint8_t test_cert_der[] = { +0x30, 0x82, 0x04, 0x09, 0x30, 0x82, 0x02, 0xf1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x09, +0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, +0x34, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e, +0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x15, +0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, +0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x34, 0x32, 0x38, 0x31, +0x30, 0x32, 0x31, 0x31, 0x31, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x34, 0x32, 0x38, 0x31, 0x30, +0x32, 0x31, 0x31, 0x31, 0x5a, 0x30, 0x32, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, +0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e, 0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, +0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x69, 0x70, 0x61, 0x2d, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2e, +0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, +0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, +0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb2, 0x32, 0x92, 0xab, 0x47, 0xb8, +0x0c, 0x13, 0x54, 0x4a, 0x1f, 0x1e, 0x29, 0x06, 0xff, 0xd0, 0x50, 0xcb, 0xf7, 0x5f, 0x79, 0x91, +0x65, 0xb1, 0x39, 0x01, 0x83, 0x6a, 0xad, 0x9e, 0x77, 0x3b, 0xf3, 0x0d, 0xd7, 0xb9, 0xf6, 0xdc, +0x9e, 0x4a, 0x49, 0xa7, 0xd0, 0x66, 0x72, 0xcc, 0xbf, 0x77, 0xd6, 0xde, 0xa9, 0xfe, 0x67, 0x96, +0xcc, 0x49, 0xf1, 0x37, 0x23, 0x2e, 0xc4, 0x50, 0xf4, 0xeb, 0xba, 0x62, 0xd4, 0x23, 0x4d, 0xf3, +0x37, 0x38, 0x82, 0xee, 0x3b, 0x3f, 0x2c, 0xd0, 0x80, 0x9b, 0x17, 0xaa, 0x9b, 0xeb, 0xa6, 0xdd, +0xf6, 0x15, 0xff, 0x06, 0xb2, 0xce, 0xff, 0xdf, 0x8a, 0x9e, 0x95, 0x85, 0x49, 0x1f, 0x84, 0xfd, +0x81, 0x26, 0xce, 0x06, 0x32, 0x0d, 0x36, 0xca, 0x7c, 0x15, 0x81, 0x68, 0x6b, 0x8f, 0x3e, 0xb3, +0xa2, 0xfc, 0xae, 0xaf, 0xc2, 0x44, 0x58, 0x15, 0x95, 0x40, 0xfc, 0x56, 0x19, 0x91, 0x80, 0xed, +0x42, 0x11, 0x66, 0x04, 0xef, 0x3c, 0xe0, 0x76, 0x33, 0x4b, 0x83, 0xfa, 0x7e, 0xb4, 0x47, 0xdc, +0xfb, 0xed, 0x46, 0xa5, 0x8d, 0x0a, 0x66, 0x87, 0xa5, 0xef, 0x7b, 0x74, 0x62, 0xac, 0xbe, 0x73, +0x36, 0xc9, 0xb4, 0xfe, 0x20, 0xc4, 0x81, 0xf3, 0xfe, 0x78, 0x19, 0xa8, 0xd0, 0xaf, 0x7f, 0x81, +0x72, 0x24, 0x61, 0xd9, 0x76, 0x93, 0xe3, 0x0b, 0xd2, 0x4f, 0x19, 0x17, 0x33, 0x57, 0xd4, 0x82, +0xb0, 0xf1, 0xa8, 0x03, 0xf6, 0x01, 0x99, 0xa9, 0xb8, 0x8c, 0x83, 0xc9, 0xba, 0x19, 0x87, 0xea, +0xd6, 0x3b, 0x06, 0xeb, 0x4c, 0xf7, 0xf1, 0xe5, 0x28, 0xa9, 0x10, 0xb6, 0x46, 0xde, 0xe1, 0xe1, +0x3f, 0xc1, 0xcc, 0x72, 0xbe, 0x2a, 0x43, 0xc6, 0xf6, 0xd0, 0xb5, 0xa0, 0xc4, 0x24, 0x6e, 0x4f, +0xbd, 0xec, 0x22, 0x8a, 0x07, 0x11, 0x3d, 0xf9, 0xd3, 0x15, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, +0x82, 0x01, 0x26, 0x30, 0x82, 0x01, 0x22, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, +0x30, 0x16, 0x80, 0x14, 0xf2, 0x9d, 0x42, 0x4e, 0x0f, 0xc4, 0x48, 0x25, 0x58, 0x2f, 0x1c, 0xce, +0x0f, 0xa1, 0x3f, 0x22, 0xc8, 0x55, 0xc8, 0x91, 0x30, 0x3b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, +0x05, 0x07, 0x01, 0x01, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, +0x05, 0x07, 0x30, 0x01, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70, 0x61, +0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x63, 0x61, +0x2f, 0x6f, 0x63, 0x73, 0x70, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, +0x04, 0x03, 0x02, 0x04, 0xf0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, +0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, +0x05, 0x07, 0x03, 0x02, 0x30, 0x74, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x6d, 0x30, 0x6b, 0x30, +0x69, 0xa0, 0x31, 0xa0, 0x2f, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70, +0x61, 0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x69, +0x70, 0x61, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x52, 0x4c, +0x2e, 0x62, 0x69, 0x6e, 0xa2, 0x34, 0xa4, 0x32, 0x30, 0x30, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, +0x55, 0x04, 0x0a, 0x0c, 0x05, 0x69, 0x70, 0x61, 0x63, 0x61, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, +0x55, 0x04, 0x03, 0x0c, 0x15, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, +0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, +0x0e, 0x04, 0x16, 0x04, 0x14, 0x2d, 0x2b, 0x3f, 0xcb, 0xf5, 0xb2, 0xff, 0x32, 0x2c, 0xa8, 0xc2, +0x1c, 0xdd, 0xbd, 0x8c, 0x80, 0x1e, 0xdd, 0x31, 0x82, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, +0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9a, 0x47, 0x2e, +0x50, 0xa7, 0x4d, 0x1d, 0x53, 0x0f, 0xc9, 0x71, 0x42, 0x0c, 0xe5, 0xda, 0x7d, 0x49, 0x64, 0xe7, +0xab, 0xc8, 0xdf, 0xdf, 0x02, 0xc1, 0x87, 0xd1, 0x5b, 0xde, 0xda, 0x6f, 0x2b, 0xe4, 0xf0, 0xbe, +0xba, 0x09, 0xdf, 0x02, 0x85, 0x0b, 0x8a, 0xe6, 0x9b, 0x06, 0x7d, 0x69, 0x38, 0x6c, 0x72, 0xff, +0x4c, 0x7b, 0x2a, 0x0d, 0x3f, 0x23, 0x2f, 0x16, 0x46, 0xff, 0x05, 0x93, 0xb0, 0xea, 0x24, 0x28, +0xd7, 0x12, 0xa1, 0x57, 0xb8, 0x59, 0x19, 0x25, 0xf3, 0x43, 0x0a, 0xd3, 0xfd, 0x0f, 0x37, 0x8d, +0xb8, 0xca, 0x15, 0xe7, 0x48, 0x8a, 0xa0, 0xc7, 0xc7, 0x4b, 0x7f, 0x01, 0x3c, 0x58, 0xd7, 0x37, +0xe5, 0xff, 0x7d, 0x2b, 0x01, 0xac, 0x0d, 0x9f, 0x51, 0x6a, 0xe5, 0x40, 0x24, 0xe6, 0x5e, 0x55, +0x0d, 0xf7, 0xb8, 0x2f, 0x42, 0xac, 0x6d, 0xe5, 0x29, 0x6b, 0xc6, 0x0b, 0xa4, 0xbf, 0x19, 0xbd, +0x39, 0x27, 0xee, 0xfe, 0xc5, 0xb3, 0xdb, 0x62, 0xd4, 0xbe, 0xd2, 0x47, 0xba, 0x96, 0x30, 0x5a, +0xfd, 0x62, 0x00, 0xb8, 0x27, 0x5d, 0x2f, 0x3a, 0x94, 0x0b, 0x95, 0x35, 0x85, 0x40, 0x2c, 0xbc, +0x67, 0xdf, 0x8a, 0xf9, 0xf1, 0x7b, 0x19, 0x96, 0x3e, 0x42, 0x48, 0x13, 0x23, 0x04, 0x95, 0xa9, +0x6b, 0x11, 0x33, 0x81, 0x47, 0x5a, 0x83, 0x72, 0xf6, 0x20, 0xfa, 0x8e, 0x41, 0x7b, 0x8f, 0x77, +0x47, 0x7c, 0xc7, 0x5d, 0x46, 0xf4, 0x4f, 0xfd, 0x81, 0x0a, 0xae, 0x39, 0x27, 0xb6, 0x6a, 0x26, +0x63, 0xb1, 0xd3, 0xbf, 0x55, 0x83, 0x82, 0x9b, 0x36, 0x6c, 0x33, 0x64, 0x0f, 0x50, 0xc0, 0x55, +0x94, 0x13, 0xc3, 0x85, 0xf4, 0xd5, 0x71, 0x65, 0xd0, 0xc0, 0xdd, 0xfc, 0xe6, 0xec, 0x9c, 0x5b, +0xf0, 0x11, 0xb5, 0x2c, 0xf3, 0x48, 0xc1, 0x36, 0x8c, 0xa2, 0x96, 0x48, 0x84}; + +#define TEST_CERT_PEM "-----BEGIN CERTIFICATE-----\n" \ +"MIIECTCCAvGgAwIBAgIBCTANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu\n" \ +"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA0Mjgx\n" \ +"MDIxMTFaFw0xNzA0MjgxMDIxMTFaMDIxEjAQBgNVBAoMCUlQQS5ERVZFTDEcMBoG\n" \ +"A1UEAwwTaXBhLWRldmVsLmlwYS5kZXZlbDCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" \ +"ADCCAQoCggEBALIykqtHuAwTVEofHikG/9BQy/dfeZFlsTkBg2qtnnc78w3Xufbc\n" \ +"nkpJp9Bmcsy/d9beqf5nlsxJ8TcjLsRQ9Ou6YtQjTfM3OILuOz8s0ICbF6qb66bd\n" \ +"9hX/BrLO/9+KnpWFSR+E/YEmzgYyDTbKfBWBaGuPPrOi/K6vwkRYFZVA/FYZkYDt\n" \ +"QhFmBO884HYzS4P6frRH3PvtRqWNCmaHpe97dGKsvnM2ybT+IMSB8/54GajQr3+B\n" \ +"ciRh2XaT4wvSTxkXM1fUgrDxqAP2AZmpuIyDyboZh+rWOwbrTPfx5SipELZG3uHh\n" \ +"P8HMcr4qQ8b20LWgxCRuT73sIooHET350xUCAwEAAaOCASYwggEiMB8GA1UdIwQY\n" \ +"MBaAFPKdQk4PxEglWC8czg+hPyLIVciRMDsGCCsGAQUFBwEBBC8wLTArBggrBgEF\n" \ +"BQcwAYYfaHR0cDovL2lwYS1jYS5pcGEuZGV2ZWwvY2Evb2NzcDAOBgNVHQ8BAf8E\n" \ +"BAMCBPAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHQGA1UdHwRtMGsw\n" \ +"aaAxoC+GLWh0dHA6Ly9pcGEtY2EuaXBhLmRldmVsL2lwYS9jcmwvTWFzdGVyQ1JM\n" \ +"LmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRl\n" \ +"IEF1dGhvcml0eTAdBgNVHQ4EFgQULSs/y/Wy/zIsqMIc3b2MgB7dMYIwDQYJKoZI\n" \ +"hvcNAQELBQADggEBAJpHLlCnTR1TD8lxQgzl2n1JZOeryN/fAsGH0Vve2m8r5PC+\n" \ +"ugnfAoULiuabBn1pOGxy/0x7Kg0/Iy8WRv8Fk7DqJCjXEqFXuFkZJfNDCtP9DzeN\n" \ +"uMoV50iKoMfHS38BPFjXN+X/fSsBrA2fUWrlQCTmXlUN97gvQqxt5Slrxgukvxm9\n" \ +"OSfu/sWz22LUvtJHupYwWv1iALgnXS86lAuVNYVALLxn34r58XsZlj5CSBMjBJWp\n" \ +"axEzgUdag3L2IPqOQXuPd0d8x11G9E/9gQquOSe2aiZjsdO/VYOCmzZsM2QPUMBV\n" \ +"lBPDhfTVcWXQwN385uycW/ARtSzzSME2jKKWSIQ=\n" \ +"-----END CERTIFICATE-----\n" + +#define TEST_CERT_PEM_WITH_METADATA "Bag Attributes\n" \ +" friendlyName: ipa-devel\n" \ +" localKeyID: 8E 0D 04 1F BC 13 73 54 00 8F 65 57 D7 A8 AF 34 0C 18 B3 99\n" \ +"subject= /O=IPA.DEVEL/CN=ipa-devel.ipa.devel\n" \ +"issuer= /O=IPA.DEVEL/CN=Certificate Authority\n" \ +TEST_CERT_PEM + +#define TEST_CERT_DERB64 \ +"MIIECTCCAvGgAwIBAgIBCTANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \ +"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA0Mjgx" \ +"MDIxMTFaFw0xNzA0MjgxMDIxMTFaMDIxEjAQBgNVBAoMCUlQQS5ERVZFTDEcMBoG" \ +"A1UEAwwTaXBhLWRldmVsLmlwYS5kZXZlbDCCASIwDQYJKoZIhvcNAQEBBQADggEP" \ +"ADCCAQoCggEBALIykqtHuAwTVEofHikG/9BQy/dfeZFlsTkBg2qtnnc78w3Xufbc" \ +"nkpJp9Bmcsy/d9beqf5nlsxJ8TcjLsRQ9Ou6YtQjTfM3OILuOz8s0ICbF6qb66bd" \ +"9hX/BrLO/9+KnpWFSR+E/YEmzgYyDTbKfBWBaGuPPrOi/K6vwkRYFZVA/FYZkYDt" \ +"QhFmBO884HYzS4P6frRH3PvtRqWNCmaHpe97dGKsvnM2ybT+IMSB8/54GajQr3+B" \ +"ciRh2XaT4wvSTxkXM1fUgrDxqAP2AZmpuIyDyboZh+rWOwbrTPfx5SipELZG3uHh" \ +"P8HMcr4qQ8b20LWgxCRuT73sIooHET350xUCAwEAAaOCASYwggEiMB8GA1UdIwQY" \ +"MBaAFPKdQk4PxEglWC8czg+hPyLIVciRMDsGCCsGAQUFBwEBBC8wLTArBggrBgEF" \ +"BQcwAYYfaHR0cDovL2lwYS1jYS5pcGEuZGV2ZWwvY2Evb2NzcDAOBgNVHQ8BAf8E" \ +"BAMCBPAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHQGA1UdHwRtMGsw" \ +"aaAxoC+GLWh0dHA6Ly9pcGEtY2EuaXBhLmRldmVsL2lwYS9jcmwvTWFzdGVyQ1JM" \ +"LmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRl" \ +"IEF1dGhvcml0eTAdBgNVHQ4EFgQULSs/y/Wy/zIsqMIc3b2MgB7dMYIwDQYJKoZI" \ +"hvcNAQELBQADggEBAJpHLlCnTR1TD8lxQgzl2n1JZOeryN/fAsGH0Vve2m8r5PC+" \ +"ugnfAoULiuabBn1pOGxy/0x7Kg0/Iy8WRv8Fk7DqJCjXEqFXuFkZJfNDCtP9DzeN" \ +"uMoV50iKoMfHS38BPFjXN+X/fSsBrA2fUWrlQCTmXlUN97gvQqxt5Slrxgukvxm9" \ +"OSfu/sWz22LUvtJHupYwWv1iALgnXS86lAuVNYVALLxn34r58XsZlj5CSBMjBJWp" \ +"axEzgUdag3L2IPqOQXuPd0d8x11G9E/9gQquOSe2aiZjsdO/VYOCmzZsM2QPUMBV" \ +"lBPDhfTVcWXQwN385uycW/ARtSzzSME2jKKWSIQ=" + +struct test_state { + void *dummy; + bool done; + struct sss_certmap_ctx *sss_certmap_ctx; +}; + +static int setup(void **state) +{ + struct test_state *ts = NULL; + + assert_true(leak_check_setup()); + + ts = talloc(global_talloc_context, struct test_state); + assert_non_null(ts); + + check_leaks_push(ts); + *state = (void *)ts; + return 0; +} + +static int teardown(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + + assert_non_null(ts); + + assert_true(check_leaks_pop(ts)); + talloc_free(ts); + assert_true(leak_check_teardown()); + return 0; +} + +void test_sss_cert_der_to_pem(void **state) +{ + int ret; + char *pem_str; + size_t pem_size; + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + + ret = sss_cert_der_to_pem(NULL, NULL, 0, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_cert_der_to_pem(ts, test_cert_der, sizeof(test_cert_der), + &pem_str, &pem_size); + assert_int_equal(ret, EOK); + assert_int_equal(sizeof(TEST_CERT_PEM) - 1, pem_size); + assert_string_equal(pem_str, TEST_CERT_PEM); + + talloc_free(pem_str); +} + +void test_sss_cert_pem_to_der(void **state) +{ + int ret; + uint8_t *der; + size_t der_size; + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + + ret = sss_cert_pem_to_der(NULL, NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_cert_pem_to_der(ts, TEST_CERT_PEM, &der, &der_size); + assert_int_equal(ret, EOK); + assert_int_equal(sizeof(test_cert_der), der_size); + assert_memory_equal(der, test_cert_der, der_size); + + talloc_free(der); + + /* https://github.com/SSSD/sssd/issues/4384 + https://tools.ietf.org/html/rfc7468#section-2 */ + ret = sss_cert_pem_to_der(ts, TEST_CERT_PEM_WITH_METADATA, &der, &der_size); + assert_int_equal(ret, EOK); + assert_int_equal(sizeof(test_cert_der), der_size); + assert_memory_equal(der, test_cert_der, der_size); + + talloc_free(der); +} + +void test_sss_cert_derb64_to_pem(void **state) +{ + int ret; + char *pem_str; + size_t pem_size; + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + + ret = sss_cert_derb64_to_pem(NULL, NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_cert_derb64_to_pem(ts, TEST_CERT_DERB64, &pem_str, &pem_size); + assert_int_equal(ret, EOK); + assert_int_equal(sizeof(TEST_CERT_PEM) - 1, pem_size); + assert_string_equal(pem_str, TEST_CERT_PEM); + + talloc_free(pem_str); +} + +void test_sss_cert_pem_to_derb64(void **state) +{ + int ret; + char *derb64; + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + + ret = sss_cert_pem_to_derb64(NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_cert_pem_to_derb64(ts, TEST_CERT_PEM, &derb64); + assert_int_equal(ret, EOK); + assert_string_equal(derb64, TEST_CERT_DERB64); + + talloc_free(derb64); +} + +void test_bin_to_ldap_filter_value(void **state) +{ + int ret; + size_t c; + char *str; + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + + struct test_data { + uint8_t blob[5]; + const char *str; + } test_data[] = { + {{0x01, 0x02, 0x03, 0x04, 0x05}, "\\01\\02\\03\\04\\05"}, + {{0x00, 0x00, 0x00, 0x00, 0x00}, "\\00\\00\\00\\00\\00"}, + {{0xff, 0xff, 0xff, 0xff, 0xff}, "\\ff\\ff\\ff\\ff\\ff"}, + {{0xca, 0xfe, 0xc0, 0xff, 0xee}, "\\ca\\fe\\c0\\ff\\ee"}, + {{0}, NULL} + }; + + ret = bin_to_ldap_filter_value(ts, NULL, 0, NULL); + assert_int_equal(ret, EINVAL); + + for (c = 0; test_data[c].str != NULL; c++) { + ret = bin_to_ldap_filter_value(ts, test_data[c].blob, 5, &str); + assert_int_equal(ret, EOK); + assert_string_equal(str, test_data[c].str); + + talloc_free(str); + } + +} + +void test_sss_cert_derb64_to_ldap_filter(void **state) +{ + int ret; + char *filter; + + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + + ret = sss_cert_derb64_to_ldap_filter(ts, NULL, NULL, NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_cert_derb64_to_ldap_filter(ts, "AAECAwQFBgcICQ==", "attrName", + NULL, NULL, &filter); + assert_int_equal(ret, EOK); + assert_string_equal(filter, + "(attrName=\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09)"); + + talloc_free(filter); +} + +void test_pss_cert_to_ssh_key_done(struct tevent_req *req) +{ + int ret; + struct test_state *ts = tevent_req_callback_data(req, struct test_state); + struct ldb_val *keys; + uint8_t *exp_key; + size_t exp_key_size; + size_t valid_keys; + + assert_non_null(ts); + ts->done = true; + + ret = cert_to_ssh_key_recv(req, ts, &keys, &valid_keys); + talloc_free(req); + assert_int_equal(ret, 0); + assert_non_null(keys[0].data); + assert_int_equal(valid_keys, 1); + + exp_key = sss_base64_decode(ts, SSSD_TEST_CERT_SSH_KEY_0007, &exp_key_size); + assert_non_null(exp_key); + assert_int_equal(keys[0].length, exp_key_size); + assert_memory_equal(keys[0].data, exp_key, exp_key_size); + + talloc_free(exp_key); + talloc_free(keys); +} + +void test_pss_cert_to_ssh_key_send(void **state) +{ + struct tevent_context *ev; + struct tevent_req *req; + struct ldb_val val[1]; + + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + ts->done = false; + + val[0].data = sss_base64_decode(ts, SSSD_TEST_CERT_0007, &val[0].length); + assert_non_null(val[0].data); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + req = cert_to_ssh_key_send(ts, ev, NULL, P11_CHILD_TIMEOUT, + ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA.pem", + NULL, 1, &val[0], NULL); + assert_non_null(req); + + tevent_req_set_callback(req, test_pss_cert_to_ssh_key_done, ts); + + while (!ts->done) { + tevent_loop_once(ev); + } + + talloc_free(val[0].data); + talloc_free(ev); +} + +void test_cert_to_ssh_key_done(struct tevent_req *req) +{ + int ret; + struct test_state *ts = tevent_req_callback_data(req, struct test_state); + struct ldb_val *keys; + uint8_t *exp_key; + size_t exp_key_size; + size_t valid_keys; + + assert_non_null(ts); + ts->done = true; + + ret = cert_to_ssh_key_recv(req, ts, &keys, &valid_keys); + talloc_free(req); + assert_int_equal(ret, 0); + assert_non_null(keys[0].data); + assert_int_equal(valid_keys, 1); + + exp_key = sss_base64_decode(ts, SSSD_TEST_CERT_SSH_KEY_0001, &exp_key_size); + assert_non_null(exp_key); + assert_int_equal(keys[0].length, exp_key_size); + assert_memory_equal(keys[0].data, exp_key, exp_key_size); + + talloc_free(exp_key); + talloc_free(keys); +} + +void test_cert_to_ssh_key_send(void **state) +{ + struct tevent_context *ev; + struct tevent_req *req; + struct ldb_val val[1]; + + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + ts->done = false; + + val[0].data = sss_base64_decode(ts, SSSD_TEST_CERT_0001, &val[0].length); + assert_non_null(val[0].data); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + req = cert_to_ssh_key_send(ts, ev, NULL, P11_CHILD_TIMEOUT, + ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA.pem", + NULL, 1, &val[0], NULL); + assert_non_null(req); + + tevent_req_set_callback(req, test_cert_to_ssh_key_done, ts); + + while (!ts->done) { + tevent_loop_once(ev); + } + + talloc_free(val[0].data); + talloc_free(ev); +} + +void test_cert_to_ssh_2keys_done(struct tevent_req *req) +{ + int ret; + struct test_state *ts = tevent_req_callback_data(req, struct test_state); + struct ldb_val *keys; + uint8_t *exp_key; + size_t exp_key_size; + size_t valid_keys; + + assert_non_null(ts); + ts->done = true; + + ret = cert_to_ssh_key_recv(req, ts, &keys, &valid_keys); + talloc_free(req); + assert_int_equal(ret, 0); + assert_non_null(keys[0].data); + assert_non_null(keys[1].data); + assert_int_equal(valid_keys, 2); + + exp_key = sss_base64_decode(ts, SSSD_TEST_CERT_SSH_KEY_0001, &exp_key_size); + assert_non_null(exp_key); + assert_int_equal(keys[0].length, exp_key_size); + assert_memory_equal(keys[0].data, exp_key, exp_key_size); + talloc_free(exp_key); + + exp_key = sss_base64_decode(ts, SSSD_TEST_CERT_SSH_KEY_0002, &exp_key_size); + assert_non_null(exp_key); + assert_int_equal(keys[1].length, exp_key_size); + assert_memory_equal(keys[1].data, exp_key, exp_key_size); + talloc_free(exp_key); + + talloc_free(keys); +} + +void test_cert_to_ssh_2keys_send(void **state) +{ + struct tevent_context *ev; + struct tevent_req *req; + struct ldb_val val[2]; + + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + ts->done = false; + + val[0].data = sss_base64_decode(ts, SSSD_TEST_CERT_0001, + &val[0].length); + assert_non_null(val[0].data); + + val[1].data = sss_base64_decode(ts, SSSD_TEST_CERT_0002, + &val[1].length); + assert_non_null(val[1].data); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + req = cert_to_ssh_key_send(ts, ev, NULL, P11_CHILD_TIMEOUT, + ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA.pem", + NULL, 2, &val[0], NULL); + assert_non_null(req); + + tevent_req_set_callback(req, test_cert_to_ssh_2keys_done, ts); + + while (!ts->done) { + tevent_loop_once(ev); + } + + talloc_free(val[0].data); + talloc_free(val[1].data); + talloc_free(ev); +} + +void test_cert_to_ssh_2keys_invalid_done(struct tevent_req *req) +{ + int ret; + struct test_state *ts = tevent_req_callback_data(req, struct test_state); + struct ldb_val *keys; + uint8_t *exp_key; + size_t exp_key_size; + size_t valid_keys; + + assert_non_null(ts); + ts->done = true; + + ret = cert_to_ssh_key_recv(req, ts, &keys, &valid_keys); + talloc_free(req); + assert_int_equal(ret, 0); + assert_non_null(keys[0].data); + assert_null(keys[1].data); + assert_int_equal(keys[1].length, 0); + assert_non_null(keys[2].data); + assert_int_equal(valid_keys, 2); + + exp_key = sss_base64_decode(ts, SSSD_TEST_CERT_SSH_KEY_0001, &exp_key_size); + assert_non_null(exp_key); + assert_int_equal(keys[0].length, exp_key_size); + assert_memory_equal(keys[0].data, exp_key, exp_key_size); + talloc_free(exp_key); + + exp_key = sss_base64_decode(ts, SSSD_TEST_CERT_SSH_KEY_0002, &exp_key_size); + assert_non_null(exp_key); + assert_int_equal(keys[2].length, exp_key_size); + assert_memory_equal(keys[2].data, exp_key, exp_key_size); + talloc_free(exp_key); + + talloc_free(keys); +} + +void test_cert_to_ssh_2keys_invalid_send(void **state) +{ + struct tevent_context *ev; + struct tevent_req *req; + struct ldb_val val[3]; + + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + ts->done = false; + + val[0].data = sss_base64_decode(ts, SSSD_TEST_CERT_0001, + &val[0].length); + assert_non_null(val[0].data); + + val[1].data = sss_base64_decode(ts, SSSD_TEST_CERT_0002, + &val[1].length); + assert_non_null(val[1].data); + /* flip last bit to make the certificate invalid */ + val[1].data[val[1].length - 1] ^= 1 << 0; + + val[2].data = sss_base64_decode(ts, SSSD_TEST_CERT_0002, + &val[2].length); + assert_non_null(val[2].data); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + req = cert_to_ssh_key_send(ts, ev, NULL, P11_CHILD_TIMEOUT, + ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA.pem", + NULL, 3, &val[0], NULL); + assert_non_null(req); + + tevent_req_set_callback(req, test_cert_to_ssh_2keys_invalid_done, ts); + + while (!ts->done) { + tevent_loop_once(ev); + } + + talloc_free(val[0].data); + talloc_free(val[1].data); + talloc_free(val[2].data); + talloc_free(ev); +} + +void test_ec_cert_to_ssh_key_done(struct tevent_req *req) +{ + int ret; + struct test_state *ts = tevent_req_callback_data(req, struct test_state); + struct ldb_val *keys; + uint8_t *exp_key; + size_t exp_key_size; + size_t valid_keys; + + assert_non_null(ts); + ts->done = true; + + ret = cert_to_ssh_key_recv(req, ts, &keys, &valid_keys); + talloc_free(req); + assert_int_equal(ret, 0); + assert_non_null(keys[0].data); + assert_int_equal(valid_keys, 1); + + exp_key = sss_base64_decode(ts, SSSD_TEST_ECC_CERT_SSH_KEY_0001, + &exp_key_size); + assert_non_null(exp_key); + assert_int_equal(keys[0].length, exp_key_size); + assert_memory_equal(keys[0].data, exp_key, exp_key_size); + + talloc_free(exp_key); + talloc_free(keys); +} + +void test_ec_cert_to_ssh_key_send(void **state) +{ + struct tevent_context *ev; + struct tevent_req *req; + struct ldb_val val[1]; + + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + ts->done = false; + + val[0].data = sss_base64_decode(ts, SSSD_TEST_ECC_CERT_0001, + &val[0].length); + assert_non_null(val[0].data); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + req = cert_to_ssh_key_send(ts, ev, NULL, P11_CHILD_TIMEOUT, + ABS_BUILD_DIR "/src/tests/test_ECC_CA/SSSD_test_ECC_CA.pem", + NULL, 1, &val[0], NULL); + assert_non_null(req); + + tevent_req_set_callback(req, test_ec_cert_to_ssh_key_done, ts); + + while (!ts->done) { + tevent_loop_once(ev); + } + + talloc_free(val[0].data); + talloc_free(ev); +} + +void test_cert_to_ssh_2keys_with_certmap_done(struct tevent_req *req) +{ + int ret; + struct test_state *ts = tevent_req_callback_data(req, struct test_state); + struct ldb_val *keys; + uint8_t *exp_key; + size_t exp_key_size; + size_t valid_keys; + + assert_non_null(ts); + ts->done = true; + + ret = cert_to_ssh_key_recv(req, ts, &keys, &valid_keys); + talloc_free(req); + assert_int_equal(ret, 0); + assert_non_null(keys[0].data); + assert_int_equal(valid_keys, 1); + + exp_key = sss_base64_decode(ts, SSSD_TEST_CERT_SSH_KEY_0001, &exp_key_size); + assert_non_null(exp_key); + assert_int_equal(keys[0].length, exp_key_size); + assert_memory_equal(keys[0].data, exp_key, exp_key_size); + talloc_free(exp_key); + + talloc_free(keys); + sss_certmap_free_ctx(ts->sss_certmap_ctx); +} + +void test_cert_to_ssh_2keys_with_certmap_send(void **state) +{ + int ret; + struct tevent_context *ev; + struct tevent_req *req; + struct ldb_val val[2]; + + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + ts->done = false; + + ret = sss_certmap_init(ts, NULL, NULL, &ts->sss_certmap_ctx); + assert_int_equal(ret, EOK); + + ret = sss_certmap_add_rule(ts->sss_certmap_ctx, -1, + "<SUBJECT>CN=SSSD test cert 0001,.*", NULL, + NULL); + assert_int_equal(ret, EOK); + + val[0].data = sss_base64_decode(ts, SSSD_TEST_CERT_0001, + &val[0].length); + assert_non_null(val[0].data); + + val[1].data = sss_base64_decode(ts, SSSD_TEST_CERT_0002, + &val[1].length); + assert_non_null(val[1].data); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + req = cert_to_ssh_key_send(ts, ev, NULL, P11_CHILD_TIMEOUT, + ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA.pem", + ts->sss_certmap_ctx, 2, &val[0], NULL); + assert_non_null(req); + + tevent_req_set_callback(req, test_cert_to_ssh_2keys_with_certmap_done, ts); + + while (!ts->done) { + tevent_loop_once(ev); + } + + talloc_free(val[0].data); + talloc_free(val[1].data); + talloc_free(ev); +} + +void test_cert_to_ssh_2keys_with_certmap_2_done(struct tevent_req *req) +{ + int ret; + struct test_state *ts = tevent_req_callback_data(req, struct test_state); + struct ldb_val *keys; + uint8_t *exp_key; + size_t exp_key_size; + size_t valid_keys; + + assert_non_null(ts); + ts->done = true; + + ret = cert_to_ssh_key_recv(req, ts, &keys, &valid_keys); + talloc_free(req); + assert_int_equal(ret, 0); + assert_non_null(keys[0].data); + assert_int_equal(valid_keys, 1); + + exp_key = sss_base64_decode(ts, SSSD_TEST_CERT_SSH_KEY_0002, &exp_key_size); + assert_non_null(exp_key); + assert_int_equal(keys[0].length, exp_key_size); + assert_memory_equal(keys[0].data, exp_key, exp_key_size); + talloc_free(exp_key); + + talloc_free(keys); + sss_certmap_free_ctx(ts->sss_certmap_ctx); +} + +void test_cert_to_ssh_2keys_with_certmap_2_send(void **state) +{ + int ret; + struct tevent_context *ev; + struct tevent_req *req; + struct ldb_val val[2]; + + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + assert_non_null(ts); + ts->done = false; + + ret = sss_certmap_init(ts, NULL, NULL, &ts->sss_certmap_ctx); + assert_int_equal(ret, EOK); + + ret = sss_certmap_add_rule(ts->sss_certmap_ctx, -1, + "<SUBJECT>CN=SSSD test cert 0002,.*", NULL, + NULL); + assert_int_equal(ret, EOK); + + val[0].data = sss_base64_decode(ts, SSSD_TEST_CERT_0001, + &val[0].length); + assert_non_null(val[0].data); + + val[1].data = sss_base64_decode(ts, SSSD_TEST_CERT_0002, + &val[1].length); + assert_non_null(val[1].data); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + req = cert_to_ssh_key_send(ts, ev, NULL, P11_CHILD_TIMEOUT, + ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA.pem", + ts->sss_certmap_ctx, 2, &val[0], NULL); + assert_non_null(req); + + tevent_req_set_callback(req, test_cert_to_ssh_2keys_with_certmap_2_done, ts); + + while (!ts->done) { + tevent_loop_once(ev); + } + + talloc_free(val[0].data); + talloc_free(val[1].data); + talloc_free(ev); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int ret; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sss_cert_der_to_pem, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_cert_pem_to_der, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_cert_derb64_to_pem, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_cert_pem_to_derb64, + setup, teardown), + cmocka_unit_test_setup_teardown(test_bin_to_ldap_filter_value, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_cert_derb64_to_ldap_filter, + setup, teardown), +#ifdef HAVE_TEST_CA + cmocka_unit_test_setup_teardown(test_cert_to_ssh_key_send, + setup, teardown), + cmocka_unit_test_setup_teardown(test_cert_to_ssh_2keys_send, + setup, teardown), + cmocka_unit_test_setup_teardown(test_cert_to_ssh_2keys_invalid_send, + setup, teardown), + cmocka_unit_test_setup_teardown(test_ec_cert_to_ssh_key_send, + setup, teardown), + cmocka_unit_test_setup_teardown(test_pss_cert_to_ssh_key_send, + setup, teardown), + cmocka_unit_test_setup_teardown(test_cert_to_ssh_2keys_with_certmap_send, + setup, teardown), + cmocka_unit_test_setup_teardown(test_cert_to_ssh_2keys_with_certmap_2_send, + setup, teardown), +#endif + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + ret = cmocka_run_group_tests(tests, NULL, NULL); + + CRYPTO_cleanup_all_ex_data(); /* to make Valgrind happy */ + + return ret; +} diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c new file mode 100644 index 0000000..a15984d --- /dev/null +++ b/src/tests/cmocka/test_certmap.c @@ -0,0 +1,2947 @@ +/* + SSSD + + certmap - Tests for SSSD's certificate mapping library + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2017 Red Hat + + 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> + +#include "lib/certmap/sss_certmap.h" +#include "lib/certmap/sss_certmap_int.h" + +#include "util/crypto/sss_crypto.h" + +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" + +#include <openssl/crypto.h> + +#ifdef HAVE_TEST_CA +#include "tests/test_CA/SSSD_test_cert_x509_0001.h" +#include "tests/test_CA/SSSD_test_cert_x509_0003.h" +#include "tests/test_CA/SSSD_test_cert_x509_0004.h" +#else +#define SSSD_TEST_CERT_0001 "" +#define SSSD_TEST_CERT_0003 "" +#define SSSD_TEST_CERT_0004 "" +#endif + +struct priv_sss_debug { + int level; +}; + +void ext_debug(void *private, const char *file, long line, const char *function, + const char *format, ...) +{ + va_list ap; + struct priv_sss_debug *data = private; + int level = SSSDBG_OP_FAILURE; + + if (data != NULL) { + level = data->level; + } + + if (DEBUG_IS_SET(level)) { + va_start(ap, format); + sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED, + format, ap); + va_end(ap); + } +} + +static void test_sss_certmap_init(void **state) +{ + int ret; + struct sss_certmap_ctx *ctx; + + ret = sss_certmap_init(NULL, ext_debug, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + + sss_certmap_free_ctx(ctx); +} + +static struct sss_certmap_ctx *setup_prio(const int *l) +{ + int ret; + size_t c; + struct sss_certmap_ctx *ctx; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + + for (c = 0; c < 10; c++) { + ret = sss_certmap_add_rule(ctx, l[c], NULL, NULL, NULL); + assert_int_equal(ret, EOK); + } + + return ctx; +} + +static void test_sss_certmap_add_rule(void **state) +{ + struct sss_certmap_ctx *ctx; + int i; + struct priority_list *p; + struct priority_list *last; + size_t c; + + const int tests_a[][10] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, + {1, 3, 5 ,7, 9, 0, 2, 4, 6, 8}, + {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + + const int tests_b[][10] = {{0, 0, 0, 0, 1, 1, 1, 2, 2, 2}, + {2, 2, 2, 1, 1, 1, 0, 0, 0, 0}, + {0, 1, 2, 0, 1, 2, 0, 1, 2, 0}, + {0, 2, 1, 0, 2, 1, 0, 2, 1, 0}, + {0, 1, 2, 0, 2, 1, 0, 0, 1, 2}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + + for (c = 0; tests_a[c][0] != 0 || tests_a[c][9] != 0; c++) { + ctx = setup_prio(tests_a[0]); + assert_non_null(ctx); + i = 0; + for (p = ctx->prio_list; p != NULL; p = p->next) { + assert_int_equal(i, p->priority); + assert_non_null(p->rule_list); + assert_int_equal(i, p->rule_list->priority); + assert_null(p->rule_list->prev); + assert_null(p->rule_list->next); + i++; + } + + i = 9; + for (last = ctx->prio_list; last->next != NULL; last = last->next); + for (p = last; p != NULL; p = p->prev) { + assert_int_equal(i, p->priority); + assert_int_equal(i, p->rule_list->priority); + i--; + } + + sss_certmap_free_ctx(ctx); + } + for (c = 0; tests_b[c][0] != 0 || tests_b[c][9] != 0; c++) { + ctx = setup_prio(tests_b[0]); + assert_non_null(ctx); + i = 0; + for (p = ctx->prio_list; p != NULL; p = p->next) { + assert_int_equal(i, p->priority); + assert_non_null(p->rule_list); + assert_int_equal(i, p->rule_list->priority); + assert_null(p->rule_list->prev); + assert_non_null(p->rule_list->next); + assert_ptr_equal(p->rule_list, p->rule_list->next->prev); + assert_non_null(p->rule_list->next->next); + assert_ptr_equal(p->rule_list->next, + p->rule_list->next->next->prev); + if (i == 0) { + assert_non_null(p->rule_list->next->next->next); + assert_ptr_equal(p->rule_list->next->next, + p->rule_list->next->next->next->prev); + assert_null(p->rule_list->next->next->next->next); + } else { + assert_null(p->rule_list->next->next->next); + } + i++; + } + sss_certmap_free_ctx(ctx); + } +} + +static void test_sss_certmap_add_matching_rule(void **state) +{ + struct sss_certmap_ctx *ctx; + int ret; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "fsdf", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "FDSF:fsdf", NULL, NULL); + assert_int_equal(ret, ESRCH); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<rgerge>", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "KRB5:<rgerge>", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<ISSUER>", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<SUBJECT>", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<KU>", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<KU>ddqwdq", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<KU>digitalSignature,dddq", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + + ret = sss_certmap_add_rule(ctx, 1, "<EKU>", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<EKU>dwqwqw", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<EKU>.", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<EKU>.1.2.3", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<EKU>1.2.3.", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<EKU>1.a.3", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<SAN:fwfwef>", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<SAN:rfc822Name", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + /* invalid base64 input */ + ret = sss_certmap_add_rule(ctx, 1, "<SAN:ediPartyName>...", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + /* invalid OID input */ + ret = sss_certmap_add_rule(ctx, 1, "<SAN:.>dqq", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<SAN:.1>dqq", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<SAN:1.>dqq", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<SAN:11>dqq", NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "<ISSUER>a", NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); + assert_string_equal("a", + ctx->prio_list->rule_list->parsed_match_rule->issuer->val); + talloc_free(ctx); + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + ret = sss_certmap_add_rule(ctx, 1, "&&<ISSUER>a", NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); + assert_string_equal("a", + ctx->prio_list->rule_list->parsed_match_rule->issuer->val); + talloc_free(ctx); + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + ret = sss_certmap_add_rule(ctx, 1, "KRB5:||<ISSUER>a", NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_or); + assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); + assert_string_equal("a", + ctx->prio_list->rule_list->parsed_match_rule->issuer->val); + talloc_free(ctx); + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + ret = sss_certmap_add_rule(ctx, 1, "KRB5:<ISSUER>a<SUBJECT>b", NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject); + assert_string_equal("b", + ctx->prio_list->rule_list->parsed_match_rule->subject->val); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); + assert_string_equal("a", + ctx->prio_list->rule_list->parsed_match_rule->issuer->val); + talloc_free(ctx); + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + ret = sss_certmap_add_rule(ctx, 1000, + "KRB5:<ISSUER>a<SUBJECT>b<ISSUER>c<SUBJECT>d", + NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject); + assert_string_equal("d", + ctx->prio_list->rule_list->parsed_match_rule->subject->val); + assert_string_equal("b", + ctx->prio_list->rule_list->parsed_match_rule->subject->next->val); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); + assert_string_equal("c", + ctx->prio_list->rule_list->parsed_match_rule->issuer->val); + assert_string_equal("a", + ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val); + + ret = sss_certmap_add_rule(ctx, 99, + "KRB5:<ISSUER>a<SUBJECT>b" + "<KU>dataEncipherment,cRLSign<ISSUER>c" + "<SUBJECT>d", + NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject); + assert_string_equal("d", + ctx->prio_list->rule_list->parsed_match_rule->subject->val); + assert_string_equal("b", + ctx->prio_list->rule_list->parsed_match_rule->subject->next->val); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); + assert_string_equal("c", + ctx->prio_list->rule_list->parsed_match_rule->issuer->val); + assert_string_equal("a", + ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->ku); + assert_int_equal(SSS_KU_CRL_SIGN|SSS_KU_DATA_ENCIPHERMENT, + ctx->prio_list->rule_list->parsed_match_rule->ku->ku); + + ret = sss_certmap_add_rule(ctx, 98, + "KRB5:<ISSUER>a<SUBJECT>b" + "<KU>dataEncipherment,cRLSign<ISSUER>c" + "<EKU>clientAuth,emailProtection" + "<SUBJECT>d", + NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject); + assert_string_equal("d", + ctx->prio_list->rule_list->parsed_match_rule->subject->val); + assert_string_equal("b", + ctx->prio_list->rule_list->parsed_match_rule->subject->next->val); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer); + assert_string_equal("c", + ctx->prio_list->rule_list->parsed_match_rule->issuer->val); + assert_string_equal("a", + ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->ku); + assert_int_equal(SSS_KU_CRL_SIGN|SSS_KU_DATA_ENCIPHERMENT, + ctx->prio_list->rule_list->parsed_match_rule->ku->ku); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku); + assert_true(string_in_list("1.3.6.1.5.5.7.3.2", + discard_const( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), + true)); + assert_true(string_in_list("1.3.6.1.5.5.7.3.4", + discard_const( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), + true)); + assert_null( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[2]); + + ret = sss_certmap_add_rule(ctx, 97, + "KRB5:<EKU>clientAuth,1.2.3,emailProtection", + NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku); + assert_true(string_in_list("1.3.6.1.5.5.7.3.2", + discard_const( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), + true)); + assert_true(string_in_list("1.3.6.1.5.5.7.3.4", + discard_const( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), + true)); + assert_true(string_in_list("1.2.3", + discard_const( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), + true)); + assert_null( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[3]); + + ret = sss_certmap_add_rule(ctx, 96, + "KRB5:<EKU>1.2.3", + NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku); + assert_true(string_in_list("1.2.3", + discard_const( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list), + true)); + assert_null( + ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[1]); + + /* SAN tests */ + ret = sss_certmap_add_rule(ctx, 89, "KRB5:<SAN>abc", NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt, + SAN_PRINCIPAL); + assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val, + "abc"); + + ret = sss_certmap_add_rule(ctx, 88, "KRB5:<SAN:dnsName>def", NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt, + SAN_DNS_NAME); + assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val, + "def"); + + ret = sss_certmap_add_rule(ctx, 87, "KRB5:<SAN:x400Address>aGlq", + NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt, + SAN_X400_ADDRESS); + assert_int_equal( + ctx->prio_list->rule_list->parsed_match_rule->san->bin_val_len, + 3); + assert_memory_equal( + ctx->prio_list->rule_list->parsed_match_rule->san->bin_val, + "hij", 3); + + ret = sss_certmap_add_rule(ctx, 86, "KRB5:<SAN:1.2.3.4>klm", + NULL, NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r, + relation_and); + assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san); + assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt, + SAN_STRING_OTHER_NAME); + assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val, + "klm"); + assert_string_equal("1.2.3.4", + ctx->prio_list->rule_list->parsed_match_rule->san->str_other_name_oid); + + talloc_free(ctx); +} + +static void test_check_ad_attr_name(void **state) +{ + char *res; + + res = check_ad_attr_name(NULL, NULL); + assert_null(res); + + res = check_ad_attr_name(NULL, ""); + assert_null(res); + + res = check_ad_attr_name(NULL, "dsddqwdas"); + assert_null(res); + + res = check_ad_attr_name(NULL, "dsddq=wdas"); + assert_null(res); + + res = check_ad_attr_name(NULL, "CN=abc"); + assert_null(res); + + res = check_ad_attr_name(NULL, "O=xyz"); + assert_null(res); + + res = check_ad_attr_name(NULL, "ST=def"); + assert_non_null(res); + assert_string_equal(res, "S=def"); + talloc_free(res); +} + +const uint8_t test_cert_der[] = { +0x30, 0x82, 0x04, 0x09, 0x30, 0x82, 0x02, 0xf1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x09, +0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, +0x34, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e, +0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x15, +0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, +0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x34, 0x32, 0x38, 0x31, +0x30, 0x32, 0x31, 0x31, 0x31, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x34, 0x32, 0x38, 0x31, 0x30, +0x32, 0x31, 0x31, 0x31, 0x5a, 0x30, 0x32, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, +0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e, 0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, +0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x69, 0x70, 0x61, 0x2d, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2e, +0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, +0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, +0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb2, 0x32, 0x92, 0xab, 0x47, 0xb8, +0x0c, 0x13, 0x54, 0x4a, 0x1f, 0x1e, 0x29, 0x06, 0xff, 0xd0, 0x50, 0xcb, 0xf7, 0x5f, 0x79, 0x91, +0x65, 0xb1, 0x39, 0x01, 0x83, 0x6a, 0xad, 0x9e, 0x77, 0x3b, 0xf3, 0x0d, 0xd7, 0xb9, 0xf6, 0xdc, +0x9e, 0x4a, 0x49, 0xa7, 0xd0, 0x66, 0x72, 0xcc, 0xbf, 0x77, 0xd6, 0xde, 0xa9, 0xfe, 0x67, 0x96, +0xcc, 0x49, 0xf1, 0x37, 0x23, 0x2e, 0xc4, 0x50, 0xf4, 0xeb, 0xba, 0x62, 0xd4, 0x23, 0x4d, 0xf3, +0x37, 0x38, 0x82, 0xee, 0x3b, 0x3f, 0x2c, 0xd0, 0x80, 0x9b, 0x17, 0xaa, 0x9b, 0xeb, 0xa6, 0xdd, +0xf6, 0x15, 0xff, 0x06, 0xb2, 0xce, 0xff, 0xdf, 0x8a, 0x9e, 0x95, 0x85, 0x49, 0x1f, 0x84, 0xfd, +0x81, 0x26, 0xce, 0x06, 0x32, 0x0d, 0x36, 0xca, 0x7c, 0x15, 0x81, 0x68, 0x6b, 0x8f, 0x3e, 0xb3, +0xa2, 0xfc, 0xae, 0xaf, 0xc2, 0x44, 0x58, 0x15, 0x95, 0x40, 0xfc, 0x56, 0x19, 0x91, 0x80, 0xed, +0x42, 0x11, 0x66, 0x04, 0xef, 0x3c, 0xe0, 0x76, 0x33, 0x4b, 0x83, 0xfa, 0x7e, 0xb4, 0x47, 0xdc, +0xfb, 0xed, 0x46, 0xa5, 0x8d, 0x0a, 0x66, 0x87, 0xa5, 0xef, 0x7b, 0x74, 0x62, 0xac, 0xbe, 0x73, +0x36, 0xc9, 0xb4, 0xfe, 0x20, 0xc4, 0x81, 0xf3, 0xfe, 0x78, 0x19, 0xa8, 0xd0, 0xaf, 0x7f, 0x81, +0x72, 0x24, 0x61, 0xd9, 0x76, 0x93, 0xe3, 0x0b, 0xd2, 0x4f, 0x19, 0x17, 0x33, 0x57, 0xd4, 0x82, +0xb0, 0xf1, 0xa8, 0x03, 0xf6, 0x01, 0x99, 0xa9, 0xb8, 0x8c, 0x83, 0xc9, 0xba, 0x19, 0x87, 0xea, +0xd6, 0x3b, 0x06, 0xeb, 0x4c, 0xf7, 0xf1, 0xe5, 0x28, 0xa9, 0x10, 0xb6, 0x46, 0xde, 0xe1, 0xe1, +0x3f, 0xc1, 0xcc, 0x72, 0xbe, 0x2a, 0x43, 0xc6, 0xf6, 0xd0, 0xb5, 0xa0, 0xc4, 0x24, 0x6e, 0x4f, +0xbd, 0xec, 0x22, 0x8a, 0x07, 0x11, 0x3d, 0xf9, 0xd3, 0x15, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, +0x82, 0x01, 0x26, 0x30, 0x82, 0x01, 0x22, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, +0x30, 0x16, 0x80, 0x14, 0xf2, 0x9d, 0x42, 0x4e, 0x0f, 0xc4, 0x48, 0x25, 0x58, 0x2f, 0x1c, 0xce, +0x0f, 0xa1, 0x3f, 0x22, 0xc8, 0x55, 0xc8, 0x91, 0x30, 0x3b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, +0x05, 0x07, 0x01, 0x01, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, +0x05, 0x07, 0x30, 0x01, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70, 0x61, +0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x63, 0x61, +0x2f, 0x6f, 0x63, 0x73, 0x70, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, +0x04, 0x03, 0x02, 0x04, 0xf0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, +0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, +0x05, 0x07, 0x03, 0x02, 0x30, 0x74, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x6d, 0x30, 0x6b, 0x30, +0x69, 0xa0, 0x31, 0xa0, 0x2f, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70, +0x61, 0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x69, +0x70, 0x61, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x52, 0x4c, +0x2e, 0x62, 0x69, 0x6e, 0xa2, 0x34, 0xa4, 0x32, 0x30, 0x30, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, +0x55, 0x04, 0x0a, 0x0c, 0x05, 0x69, 0x70, 0x61, 0x63, 0x61, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, +0x55, 0x04, 0x03, 0x0c, 0x15, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, +0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, +0x0e, 0x04, 0x16, 0x04, 0x14, 0x2d, 0x2b, 0x3f, 0xcb, 0xf5, 0xb2, 0xff, 0x32, 0x2c, 0xa8, 0xc2, +0x1c, 0xdd, 0xbd, 0x8c, 0x80, 0x1e, 0xdd, 0x31, 0x82, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, +0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9a, 0x47, 0x2e, +0x50, 0xa7, 0x4d, 0x1d, 0x53, 0x0f, 0xc9, 0x71, 0x42, 0x0c, 0xe5, 0xda, 0x7d, 0x49, 0x64, 0xe7, +0xab, 0xc8, 0xdf, 0xdf, 0x02, 0xc1, 0x87, 0xd1, 0x5b, 0xde, 0xda, 0x6f, 0x2b, 0xe4, 0xf0, 0xbe, +0xba, 0x09, 0xdf, 0x02, 0x85, 0x0b, 0x8a, 0xe6, 0x9b, 0x06, 0x7d, 0x69, 0x38, 0x6c, 0x72, 0xff, +0x4c, 0x7b, 0x2a, 0x0d, 0x3f, 0x23, 0x2f, 0x16, 0x46, 0xff, 0x05, 0x93, 0xb0, 0xea, 0x24, 0x28, +0xd7, 0x12, 0xa1, 0x57, 0xb8, 0x59, 0x19, 0x25, 0xf3, 0x43, 0x0a, 0xd3, 0xfd, 0x0f, 0x37, 0x8d, +0xb8, 0xca, 0x15, 0xe7, 0x48, 0x8a, 0xa0, 0xc7, 0xc7, 0x4b, 0x7f, 0x01, 0x3c, 0x58, 0xd7, 0x37, +0xe5, 0xff, 0x7d, 0x2b, 0x01, 0xac, 0x0d, 0x9f, 0x51, 0x6a, 0xe5, 0x40, 0x24, 0xe6, 0x5e, 0x55, +0x0d, 0xf7, 0xb8, 0x2f, 0x42, 0xac, 0x6d, 0xe5, 0x29, 0x6b, 0xc6, 0x0b, 0xa4, 0xbf, 0x19, 0xbd, +0x39, 0x27, 0xee, 0xfe, 0xc5, 0xb3, 0xdb, 0x62, 0xd4, 0xbe, 0xd2, 0x47, 0xba, 0x96, 0x30, 0x5a, +0xfd, 0x62, 0x00, 0xb8, 0x27, 0x5d, 0x2f, 0x3a, 0x94, 0x0b, 0x95, 0x35, 0x85, 0x40, 0x2c, 0xbc, +0x67, 0xdf, 0x8a, 0xf9, 0xf1, 0x7b, 0x19, 0x96, 0x3e, 0x42, 0x48, 0x13, 0x23, 0x04, 0x95, 0xa9, +0x6b, 0x11, 0x33, 0x81, 0x47, 0x5a, 0x83, 0x72, 0xf6, 0x20, 0xfa, 0x8e, 0x41, 0x7b, 0x8f, 0x77, +0x47, 0x7c, 0xc7, 0x5d, 0x46, 0xf4, 0x4f, 0xfd, 0x81, 0x0a, 0xae, 0x39, 0x27, 0xb6, 0x6a, 0x26, +0x63, 0xb1, 0xd3, 0xbf, 0x55, 0x83, 0x82, 0x9b, 0x36, 0x6c, 0x33, 0x64, 0x0f, 0x50, 0xc0, 0x55, +0x94, 0x13, 0xc3, 0x85, 0xf4, 0xd5, 0x71, 0x65, 0xd0, 0xc0, 0xdd, 0xfc, 0xe6, 0xec, 0x9c, 0x5b, +0xf0, 0x11, 0xb5, 0x2c, 0xf3, 0x48, 0xc1, 0x36, 0x8c, 0xa2, 0x96, 0x48, 0x84}; + +const uint8_t test_cert2_der[] = { +0x30, 0x82, 0x06, 0x98, 0x30, 0x82, 0x05, 0x80, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x61, +0x22, 0x88, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, +0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x15, 0x30, 0x13, 0x06, 0x0a, +0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x05, 0x64, 0x65, 0x76, 0x65, +0x6c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, +0x19, 0x16, 0x02, 0x61, 0x64, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f, +0x61, 0x64, 0x2d, 0x41, 0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x2d, 0x43, 0x41, 0x30, +0x1e, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x35, 0x31, 0x31, 0x31, 0x5a, +0x17, 0x0d, 0x31, 0x37, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x35, 0x31, 0x31, 0x31, 0x5a, 0x30, +0x70, 0x31, 0x15, 0x30, 0x13, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, +0x19, 0x16, 0x05, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x0a, 0x09, 0x92, +0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x02, 0x61, 0x64, 0x31, 0x0e, 0x30, 0x0c, +0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x31, 0x0c, 0x30, 0x0a, +0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x03, 0x74, 0x20, 0x75, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, +0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x16, 0x74, 0x65, 0x73, 0x74, 0x2e, +0x75, 0x73, 0x65, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, +0x6e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, +0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, +0x01, 0x00, 0x9c, 0xcf, 0x36, 0x99, 0xde, 0x63, 0x74, 0x2b, 0x77, 0x25, 0x9e, 0x24, 0xd9, 0x77, +0x4b, 0x5f, 0x98, 0xc0, 0x8c, 0xd7, 0x20, 0x91, 0xc0, 0x1c, 0xe8, 0x37, 0x45, 0xbf, 0x3c, 0xd9, +0x33, 0xbd, 0xe9, 0xde, 0xc9, 0x5d, 0xd4, 0xcd, 0x06, 0x0a, 0x0d, 0xd4, 0xf1, 0x7c, 0x74, 0x5b, +0x29, 0xd5, 0x66, 0x9c, 0x2c, 0x9f, 0x6b, 0x1a, 0x0f, 0x0d, 0xe6, 0x6c, 0x62, 0xa5, 0x41, 0x4f, +0xc3, 0xa4, 0x88, 0x27, 0x11, 0x5d, 0xb7, 0xb1, 0xfb, 0xf8, 0x8d, 0xee, 0x43, 0x8d, 0x93, 0xb5, +0x8c, 0xb4, 0x34, 0x06, 0xf5, 0xe9, 0x2f, 0x5a, 0x26, 0x68, 0xd7, 0x43, 0x60, 0x82, 0x5e, 0x22, +0xa7, 0xc6, 0x34, 0x40, 0x19, 0xa5, 0x8e, 0xf0, 0x58, 0x9f, 0x16, 0x2d, 0x43, 0x3f, 0x0c, 0xda, +0xe2, 0x23, 0xf6, 0x09, 0x2a, 0x5e, 0xbd, 0x84, 0x27, 0xc8, 0xab, 0xd5, 0x70, 0xf8, 0x3d, 0x9c, +0x14, 0xc2, 0xc2, 0xa2, 0x77, 0xe8, 0x44, 0x73, 0x10, 0x01, 0x34, 0x40, 0x1f, 0xc6, 0x2f, 0xa0, +0x70, 0xee, 0x2f, 0xd5, 0x4b, 0xbe, 0x4c, 0xc7, 0x45, 0xf7, 0xac, 0x9c, 0xc3, 0x68, 0x5b, 0x1d, +0x5a, 0x4b, 0x77, 0x65, 0x76, 0xe4, 0xb3, 0x92, 0xf4, 0x84, 0x0a, 0x9e, 0x6a, 0x9c, 0xc9, 0x53, +0x42, 0x9f, 0x6d, 0xfe, 0xf9, 0xf5, 0xf2, 0x9a, 0x15, 0x50, 0x47, 0xef, 0xf4, 0x06, 0x59, 0xc8, +0x50, 0x48, 0x4b, 0x46, 0x95, 0x68, 0x25, 0xc5, 0xbd, 0x4f, 0x65, 0x34, 0x00, 0xfc, 0x31, 0x69, +0xf8, 0x3e, 0xe0, 0x20, 0x83, 0x41, 0x27, 0x0b, 0x5c, 0x46, 0x98, 0x14, 0xf0, 0x07, 0xde, 0x02, +0x17, 0xb1, 0xd2, 0x9c, 0xbe, 0x1c, 0x0d, 0x56, 0x22, 0x1b, 0x02, 0xfe, 0xda, 0x69, 0xb9, 0xef, +0x91, 0x37, 0x39, 0x7f, 0x24, 0xda, 0xc4, 0x81, 0x5e, 0x82, 0x31, 0x2f, 0x98, 0x1d, 0xf7, 0x73, +0x5b, 0x23, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x03, 0x5d, 0x30, 0x82, 0x03, 0x59, 0x30, +0x3d, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x07, 0x04, 0x30, 0x30, 0x2e, +0x06, 0x26, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x08, 0x87, 0x85, 0xa1, 0x23, 0x84, +0xc8, 0xb2, 0x26, 0x83, 0x9d, 0x9d, 0x21, 0x82, 0xd4, 0xa6, 0x1b, 0x86, 0xa3, 0xba, 0x37, 0x81, +0x10, 0x85, 0x89, 0xd5, 0x02, 0xd6, 0x8f, 0x24, 0x02, 0x01, 0x64, 0x02, 0x01, 0x02, 0x30, 0x29, +0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x22, 0x30, 0x20, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, +0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04, 0x06, 0x0a, 0x2b, +0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x04, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, +0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x35, 0x06, 0x09, 0x2b, 0x06, 0x01, +0x04, 0x01, 0x82, 0x37, 0x15, 0x0a, 0x04, 0x28, 0x30, 0x26, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, +0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, +0x03, 0x04, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x04, +0x30, 0x81, 0x94, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x0f, 0x04, 0x81, +0x86, 0x30, 0x81, 0x83, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, +0x2a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2d, 0x30, 0x0b, +0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x16, 0x30, 0x0b, 0x06, 0x09, 0x60, +0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x19, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, +0x65, 0x03, 0x04, 0x01, 0x02, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, +0x01, 0x05, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x07, 0x30, 0x07, +0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x07, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, +0x0d, 0x03, 0x02, 0x02, 0x02, 0x00, 0x80, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, +0x0d, 0x03, 0x04, 0x02, 0x02, 0x02, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, +0x04, 0x14, 0x49, 0xac, 0xad, 0xe0, 0x65, 0x30, 0xc4, 0xce, 0xa0, 0x09, 0x03, 0x5b, 0xad, 0x4a, +0x7b, 0x49, 0x5e, 0xc9, 0x6c, 0xb4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, +0x16, 0x80, 0x14, 0x62, 0x50, 0xb6, 0x8d, 0xa1, 0xe6, 0x2d, 0x91, 0xbf, 0xb0, 0x54, 0x4d, 0x8f, +0xa8, 0xca, 0x10, 0xae, 0xb8, 0xdd, 0x54, 0x30, 0x81, 0xcc, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, +0x81, 0xc4, 0x30, 0x81, 0xc1, 0x30, 0x81, 0xbe, 0xa0, 0x81, 0xbb, 0xa0, 0x81, 0xb8, 0x86, 0x81, +0xb5, 0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x2f, 0x43, 0x4e, 0x3d, 0x61, 0x64, 0x2d, 0x41, +0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x2d, 0x43, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x61, +0x64, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x44, 0x50, 0x2c, +0x43, 0x4e, 0x3d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x25, 0x32, 0x30, 0x4b, 0x65, 0x79, 0x25, +0x32, 0x30, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65, +0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, +0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x44, 0x43, 0x3d, 0x61, 0x64, 0x2c, 0x44, 0x43, +0x3d, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x3f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, +0x74, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, +0x3f, 0x62, 0x61, 0x73, 0x65, 0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, +0x73, 0x3d, 0x63, 0x52, 0x4c, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, +0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x30, 0x81, 0xbe, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, +0x07, 0x01, 0x01, 0x04, 0x81, 0xb1, 0x30, 0x81, 0xae, 0x30, 0x81, 0xab, 0x06, 0x08, 0x2b, 0x06, +0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x81, 0x9e, 0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, +0x2f, 0x43, 0x4e, 0x3d, 0x61, 0x64, 0x2d, 0x41, 0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, +0x2d, 0x43, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x41, 0x49, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x50, 0x75, +0x62, 0x6c, 0x69, 0x63, 0x25, 0x32, 0x30, 0x4b, 0x65, 0x79, 0x25, 0x32, 0x30, 0x53, 0x65, 0x72, +0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, +0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, +0x6f, 0x6e, 0x2c, 0x44, 0x43, 0x3d, 0x61, 0x64, 0x2c, 0x44, 0x43, 0x3d, 0x64, 0x65, 0x76, 0x65, +0x6c, 0x3f, 0x63, 0x41, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x3f, +0x62, 0x61, 0x73, 0x65, 0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, +0x3d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x75, +0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x3f, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x38, +0x30, 0x36, 0xa0, 0x1c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03, +0xa0, 0x0e, 0x0c, 0x0c, 0x74, 0x75, 0x31, 0x40, 0x61, 0x64, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, +0x81, 0x16, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69, +0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, +0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x41, 0x45, 0x0a, 0x6d, +0xbb, 0x7f, 0x5c, 0x07, 0x0c, 0xc9, 0xb0, 0x39, 0x55, 0x6d, 0x7c, 0xb5, 0x02, 0xcd, 0xe8, 0xb2, +0xe5, 0x02, 0x94, 0x77, 0x60, 0xdb, 0xd1, 0xaf, 0x1d, 0xdb, 0x44, 0x5f, 0xce, 0x83, 0xdb, 0x80, +0x2e, 0xe2, 0xb2, 0x08, 0x25, 0x82, 0x14, 0xcb, 0x48, 0x95, 0x20, 0x13, 0x6c, 0xa9, 0xaa, 0xf8, +0x31, 0x56, 0xed, 0xc0, 0x3b, 0xd4, 0xae, 0x2e, 0xe3, 0x8f, 0x05, 0xfc, 0xab, 0x5f, 0x2a, 0x69, +0x23, 0xbc, 0xb8, 0x8c, 0xec, 0x2d, 0xa9, 0x0b, 0x86, 0x95, 0x73, 0x73, 0xdb, 0x17, 0xce, 0xc6, +0xae, 0xc5, 0xb4, 0xc1, 0x25, 0x87, 0x3b, 0x67, 0x43, 0x9e, 0x87, 0x5a, 0xe6, 0xb9, 0xa0, 0x28, +0x12, 0x3d, 0xa8, 0x2e, 0xd7, 0x5e, 0xef, 0x65, 0x2d, 0xe6, 0xa5, 0x67, 0x84, 0xac, 0xfd, 0x31, +0xc1, 0x78, 0xd8, 0x72, 0x51, 0xa2, 0x88, 0x55, 0x0f, 0x97, 0x47, 0x93, 0x07, 0xea, 0x8a, 0x53, +0x27, 0x4e, 0x34, 0x54, 0x34, 0x1f, 0xa0, 0x6a, 0x03, 0x44, 0xfb, 0x23, 0x61, 0x8e, 0x87, 0x8e, +0x3c, 0xd0, 0x8f, 0xae, 0xe4, 0xcf, 0xee, 0x65, 0xa8, 0xba, 0x96, 0x68, 0x08, 0x1c, 0x60, 0xe2, +0x4e, 0x11, 0xa3, 0x74, 0xb8, 0xa5, 0x4e, 0xea, 0x6a, 0x82, 0x4c, 0xc2, 0x4d, 0x63, 0x8e, 0x9f, +0x7c, 0x2f, 0xa8, 0xc0, 0x62, 0xf8, 0xf7, 0xd9, 0x25, 0xc4, 0x91, 0xab, 0x4d, 0x6a, 0x44, 0xaf, +0x75, 0x93, 0x53, 0x03, 0xa4, 0x99, 0xc8, 0xcd, 0x91, 0x89, 0x60, 0x75, 0x30, 0x99, 0x76, 0x05, +0x5a, 0xa0, 0x03, 0xa7, 0xa1, 0x2c, 0x03, 0x04, 0x8f, 0xd4, 0x5a, 0x31, 0x52, 0x28, 0x5a, 0xe6, +0xa2, 0xd3, 0x43, 0x21, 0x5b, 0xdc, 0xa2, 0x1d, 0x55, 0xa9, 0x48, 0xc5, 0xc4, 0xaa, 0xf3, 0x8b, +0xe6, 0x3e, 0x75, 0x96, 0xe4, 0x3e, 0x64, 0xaf, 0xe8, 0xa7, 0x6a, 0xb6}; + +/* used to test SAN principal encoding according to RFC4556 */ +const uint8_t test_cert3_der[] = { +0x30, 0x82, 0x03, 0x70, 0x30, 0x82, 0x02, 0x58, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, +0xe5, 0x8f, 0x16, 0xfe, 0x23, 0x4d, 0xc5, 0xd6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, +0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x1a, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, +0x04, 0x03, 0x0c, 0x0f, 0x6b, 0x72, 0x62, 0x35, 0x5f, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x5f, 0x74, +0x65, 0x73, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x37, 0x31, 0x32, 0x30, 0x39, 0x32, +0x34, 0x31, 0x38, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x34, 0x30, 0x37, 0x30, 0x39, 0x32, 0x34, +0x31, 0x38, 0x5a, 0x30, 0x1a, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, +0x6b, 0x72, 0x62, 0x35, 0x5f, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x30, +0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, +0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, +0xbf, 0x84, 0x34, 0x46, 0x37, 0x50, 0xb1, 0xca, 0x14, 0x4c, 0x6b, 0x0d, 0xe4, 0xab, 0xc1, 0xce, +0xf4, 0xd1, 0xde, 0xca, 0xf5, 0x50, 0x46, 0x3c, 0x63, 0x0f, 0x8e, 0xb8, 0xe9, 0xf9, 0x3e, 0xc4, +0xf3, 0x24, 0xc1, 0xe4, 0x78, 0xf6, 0xa4, 0x39, 0x6f, 0xc1, 0xd8, 0x9c, 0x1c, 0xa7, 0x47, 0xe4, +0xc8, 0x71, 0x32, 0x9a, 0x1d, 0x1d, 0xfb, 0x30, 0x0f, 0xf9, 0x85, 0x48, 0xf8, 0x1f, 0xa7, 0xbd, +0xda, 0x39, 0xd4, 0xc7, 0x27, 0x4f, 0xf5, 0x34, 0xee, 0x4a, 0x59, 0x0c, 0x7a, 0xec, 0x2b, 0xaf, +0x81, 0x8e, 0x41, 0x54, 0x6f, 0xcc, 0x91, 0x61, 0x4c, 0x61, 0x80, 0xca, 0x37, 0xab, 0x2c, 0x63, +0x8d, 0xce, 0x07, 0xcd, 0x61, 0x11, 0x10, 0xa0, 0xe4, 0x08, 0x7d, 0x1d, 0x10, 0x85, 0xb1, 0x64, +0x33, 0x6b, 0x4d, 0x8d, 0xd2, 0x9d, 0xd7, 0x0b, 0x21, 0xbc, 0x15, 0xcd, 0xed, 0xaa, 0xc0, 0x01, +0x67, 0xe1, 0x7c, 0xd4, 0xf7, 0xdd, 0xf8, 0x28, 0x92, 0xce, 0x8b, 0x7f, 0x08, 0x29, 0x76, 0x6e, +0xa5, 0xe6, 0xcd, 0xeb, 0x9c, 0x13, 0x78, 0xa3, 0x08, 0xb5, 0xdc, 0x7f, 0xc2, 0x60, 0xc3, 0xac, +0x68, 0x30, 0x37, 0xe1, 0x54, 0x6a, 0xa9, 0x34, 0x3e, 0x43, 0x8d, 0x6f, 0x9b, 0xe5, 0x8a, 0xf9, +0xa4, 0x22, 0xab, 0x33, 0x01, 0x32, 0xaf, 0xc4, 0x9f, 0xb1, 0x27, 0xba, 0xae, 0x20, 0x60, 0xd7, +0x16, 0x48, 0x66, 0x2b, 0x36, 0x9c, 0x54, 0xd0, 0x6e, 0x45, 0xd3, 0x23, 0x3f, 0x17, 0x2e, 0xee, +0xd4, 0x55, 0xa7, 0x75, 0x2f, 0x28, 0xa9, 0x40, 0x3b, 0xbc, 0x79, 0x69, 0xea, 0x58, 0xc2, 0x3c, +0x4c, 0x70, 0x4b, 0x93, 0xd8, 0xa4, 0xb6, 0x59, 0x24, 0x77, 0x10, 0xb3, 0xc7, 0x34, 0x99, 0x6b, +0x28, 0xbd, 0x03, 0xdb, 0xda, 0xea, 0x23, 0x19, 0x10, 0x56, 0x7e, 0xa4, 0x28, 0x04, 0x5a, 0x53, +0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xb8, 0x30, 0x81, 0xb5, 0x30, 0x09, 0x06, 0x03, 0x55, +0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, +0x02, 0x03, 0xa8, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0b, 0x30, 0x09, 0x06, 0x07, +0x2b, 0x06, 0x01, 0x05, 0x02, 0x03, 0x04, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, +0x04, 0x14, 0xea, 0xd4, 0x30, 0xd7, 0x7d, 0x3b, 0xc7, 0xb4, 0x83, 0x53, 0x2c, 0xa5, 0xb9, 0xd8, +0x1a, 0x47, 0x6b, 0xb5, 0xe5, 0x9d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, +0x16, 0x80, 0x14, 0xea, 0xd4, 0x30, 0xd7, 0x7d, 0x3b, 0xc7, 0xb4, 0x83, 0x53, 0x2c, 0xa5, 0xb9, +0xd8, 0x1a, 0x47, 0x6b, 0xb5, 0xe5, 0x9d, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x40, +0x30, 0x3e, 0xa0, 0x3c, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, 0x02, 0x02, 0xa0, 0x32, 0x30, 0x30, +0xa0, 0x0b, 0x1b, 0x09, 0x53, 0x53, 0x53, 0x44, 0x2e, 0x54, 0x45, 0x53, 0x54, 0xa1, 0x21, 0x30, +0x1f, 0xa0, 0x03, 0x02, 0x01, 0x01, 0xa1, 0x18, 0x30, 0x16, 0x1b, 0x04, 0x74, 0x65, 0x73, 0x74, +0x1b, 0x05, 0x63, 0x6f, 0x6d, 0x70, 0x32, 0x1b, 0x07, 0x61, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, +0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, +0x82, 0x01, 0x01, 0x00, 0x08, 0x64, 0x63, 0x89, 0x6d, 0x3d, 0x66, 0x77, 0xe3, 0xb6, 0x40, 0x54, +0xd7, 0xe2, 0xc5, 0x99, 0xac, 0x98, 0x6e, 0xf8, 0xcd, 0x62, 0xa4, 0xf8, 0xd9, 0xaf, 0xdb, 0xef, +0xb7, 0x10, 0x8e, 0x45, 0x42, 0x53, 0x5c, 0x3f, 0x6a, 0x8d, 0xa8, 0x8a, 0x6d, 0x76, 0x51, 0x1a, +0xf4, 0x71, 0x54, 0x27, 0x27, 0xe2, 0x45, 0xe8, 0xa8, 0xd2, 0xa9, 0xcd, 0x62, 0x0d, 0xfc, 0x0d, +0x28, 0x46, 0x9e, 0x4e, 0x5a, 0x57, 0x72, 0xb4, 0xf2, 0x35, 0x91, 0x57, 0x11, 0xae, 0x2b, 0x9c, +0x6a, 0x80, 0x21, 0x8e, 0x4c, 0x19, 0x4a, 0x2d, 0xe0, 0xd2, 0xdf, 0x83, 0x9d, 0x65, 0x49, 0xd1, +0x34, 0x34, 0x14, 0xa0, 0xbb, 0x1c, 0xa8, 0x12, 0xb0, 0xe3, 0x5e, 0x82, 0x36, 0x41, 0x4c, 0x87, +0xd1, 0x1e, 0x1a, 0xe9, 0xff, 0x55, 0xef, 0xb5, 0x2d, 0x20, 0xc5, 0xa7, 0xe5, 0x5a, 0xf2, 0xfc, +0xf7, 0xd2, 0x21, 0xc5, 0x32, 0xb4, 0x07, 0x8f, 0xc4, 0x94, 0x56, 0xa6, 0x21, 0x6a, 0xb6, 0x26, +0x05, 0x48, 0x90, 0xe0, 0x6b, 0x22, 0x35, 0x00, 0x51, 0x2e, 0xd7, 0xe8, 0x3a, 0x56, 0xa8, 0x70, +0x7d, 0x0f, 0x9a, 0x97, 0x5a, 0xb8, 0x7f, 0x33, 0xc1, 0xe0, 0x92, 0x0f, 0xb3, 0xfe, 0x36, 0xe6, +0x8b, 0x97, 0x58, 0x42, 0x49, 0xcb, 0x74, 0xde, 0x19, 0x59, 0x90, 0xb6, 0x36, 0x38, 0x07, 0x48, +0x5d, 0x5b, 0xab, 0x08, 0xf0, 0x69, 0x22, 0x42, 0x08, 0x29, 0xfe, 0x43, 0xab, 0x83, 0x73, 0x74, +0x5a, 0x3f, 0x3b, 0x5d, 0x8e, 0xca, 0x6f, 0x2d, 0xad, 0xa1, 0x6e, 0x80, 0x80, 0xd2, 0xc8, 0x16, +0xb7, 0x67, 0x1a, 0x2d, 0x37, 0x8c, 0x20, 0x3b, 0x15, 0xef, 0xb2, 0x94, 0x86, 0x5c, 0xaf, 0xa2, +0x61, 0x8b, 0xc7, 0xc1, 0xe4, 0xbe, 0x60, 0x5a, 0x86, 0x5c, 0x86, 0xba, 0x59, 0x97, 0x83, 0x1b, +0x79, 0x1c, 0x7c, 0x26}; + +#define TEST_CERT_WITH_SID_EXT \ + "MIIGFDCCBPygAwIBAgITcgAAAAIq7mYIPbH8OgAAAAAAAjANBgkqhkiG9w0BAQsF" \ + "ADBAMRIwEAYKCZImiZPyLGQBGRYCdm0xEjAQBgoJkiaJk/IsZAEZFgJhZDEWMBQG" \ + "A1UEAxMNYWQtUk9PVC1EQy1DQTAeFw0yMjA4MzEwOTA5NDFaFw0yMzA4MzEwOTA5" \ + "NDFaMBgxFjAUBgNVBAMTDXJvb3QtZGMuYWQudm0wggEiMA0GCSqGSIb3DQEBAQUA" \ + "A4IBDwAwggEKAoIBAQCrFS4l2bf9VwFl5NSFOKNcASgUwlxbdobpPQ1mB0Vso3fj" \ + "zo82O8P+zGA9E0ZcrC02w/7MUI7P2HFAyr/TFVBdSa9HM5CIT1CupzJJuLhZQ4/O" \ + "3gdy1W8aSBosorpVwS5EQYvaLrQascryTiWRu8jBNt2+/9WveMBvTXLDkj/fNK/f" \ + "7yGIFrWSjCUk37nZGpLUJQbC+0aEiOuyJn7bs2K9fN3dmZmgbwqsWBREQwhqgbCZ" \ + "5ZWbgs95JGJXScR4S4YKIkHK/hdaOEiqTCTJEpgszKBLdil6Yqt6/66b7Xun64/W" \ + "I4TfScup292WKRlfB0KVMXYxGPo2kPiI8aVwcqNJAgMBAAGjggMtMIIDKTAvBgkr" \ + "BgEEAYI3FAIEIh4gAEQAbwBtAGEAaQBuAEMAbwBuAHQAcgBvAGwAbABlAHIwHQYD" \ + "VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIFoDB4Bgkq" \ + "hkiG9w0BCQ8EazBpMA4GCCqGSIb3DQMCAgIAgDAOBggqhkiG9w0DBAICAIAwCwYJ" \ + "YIZIAWUDBAEqMAsGCWCGSAFlAwQBLTALBglghkgBZQMEAQIwCwYJYIZIAWUDBAEF" \ + "MAcGBSsOAwIHMAoGCCqGSIb3DQMHMB0GA1UdDgQWBBQURvUMyxhOgBeuK0FniBLp" \ + "ZfbqwTAfBgNVHSMEGDAWgBQbcHqjwjfOyWv8FEmhCu5zuhB8nzCBxQYDVR0fBIG9" \ + "MIG6MIG3oIG0oIGxhoGubGRhcDovLy9DTj1hZC1ST09ULURDLUNBLENOPXJvb3Qt" \ + "ZGMsQ049Q0RQLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2Vz" \ + "LENOPUNvbmZpZ3VyYXRpb24sREM9YWQsREM9dm0/Y2VydGlmaWNhdGVSZXZvY2F0" \ + "aW9uTGlzdD9iYXNlP29iamVjdENsYXNzPWNSTERpc3RyaWJ1dGlvblBvaW50MIG5" \ + "BggrBgEFBQcBAQSBrDCBqTCBpgYIKwYBBQUHMAKGgZlsZGFwOi8vL0NOPWFkLVJP" \ + "T1QtREMtQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNl" \ + "cnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9YWQsREM9dm0/Y0FDZXJ0aWZpY2F0" \ + "ZT9iYXNlP29iamVjdENsYXNzPWNlcnRpZmljYXRpb25BdXRob3JpdHkwOQYDVR0R" \ + "BDIwMKAfBgkrBgEEAYI3GQGgEgQQpJvI9IWOiU2FjSe9Y6qTk4INcm9vdC1kYy5h" \ + "ZC52bTBOBgkrBgEEAYI3GQIEQTA/oD0GCisGAQQBgjcZAgGgLwQtUy0xLTUtMjEt" \ + "Mjk0MDI2MTI2LTMzNzgyNDUwMTgtMTIwMzEwMzk0OS0xMDAxMA0GCSqGSIb3DQEB" \ + "CwUAA4IBAQBijGUmixRQ5ZY3g0+ppTcMRKKST0HE+UEUnuoBlnG3cBM4yTBBUWSg" \ + "elzAglwbZbFRWT2ieX7rZzPALNLIyr43eZFpXelZElRGnTNISj9bWV+YEQ1DVGG4" \ + "b0Z3WsrPS1DiKprgf6mNEg7bmNUcD2AYJzuFOUVVHQu+pwIOpWjVAri8MgU37f+o" \ + "eY4fwbgbYQoTb6VGk53QPajONCa0sICDasvC0BNgkfLFUsL4y7xmWEtKJl7o+3Bo" \ + "d+GO2zfbNX8oS/LjZ5f/Vpmiu04VPdiifdYAbfCQez3bevYBQn4D/bq/xVHoHSLY" \ + "x0N7c7iPuFTCbg+OVrkH3OtPuRT/4kTn" + +void test_sss_cert_get_content(void **state) +{ + int ret; + struct sss_cert_content *content; + + ret = sss_cert_get_content(NULL, test_cert_der, sizeof(test_cert_der), + &content); + assert_int_equal(ret , 0); + assert_non_null(content); + assert_non_null(content->issuer_str); + assert_string_equal(content->issuer_str, "CN=Certificate Authority,O=IPA.DEVEL"); + assert_non_null(content->subject_str); + assert_string_equal(content->subject_str, "CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_int_equal(content->key_usage, SSS_KU_DIGITAL_SIGNATURE + |SSS_KU_NON_REPUDIATION + |SSS_KU_KEY_ENCIPHERMENT + |SSS_KU_DATA_ENCIPHERMENT); + assert_non_null(content->extended_key_usage_oids); + assert_non_null(content->extended_key_usage_oids[0]); + assert_true(string_in_list("1.3.6.1.5.5.7.3.1", + discard_const(content->extended_key_usage_oids), true)); + assert_true(string_in_list("1.3.6.1.5.5.7.3.2", + discard_const(content->extended_key_usage_oids), true)); + assert_null(content->extended_key_usage_oids[2]); + assert_int_equal(content->cert_der_size, sizeof(test_cert_der)); + assert_memory_equal(content->cert_der, test_cert_der, sizeof(test_cert_der)); + + assert_non_null(content->issuer_rdn_list); + assert_string_equal(content->issuer_rdn_list[0], "O=IPA.DEVEL"); + assert_string_equal(content->issuer_rdn_list[1], "CN=Certificate Authority"); + assert_null(content->issuer_rdn_list[2]); + + assert_non_null(content->subject_rdn_list); + assert_string_equal(content->subject_rdn_list[0], "O=IPA.DEVEL"); + assert_string_equal(content->subject_rdn_list[1], "CN=ipa-devel.ipa.devel"); + assert_null(content->subject_rdn_list[2]); + + assert_int_equal(content->serial_number_size, 1); + assert_non_null(content->serial_number); + assert_memory_equal(content->serial_number, "\x09", 1); + assert_string_equal(content->serial_number_dec_str, "9"); + + assert_int_equal(content->subject_key_id_size, 20); + assert_non_null(content->subject_key_id); + assert_memory_equal(content->subject_key_id, "\x2D\x2B\x3F\xCB\xF5\xB2\xFF\x32\x2C\xA8\xC2\x1C\xDD\xBD\x8C\x80\x1E\xDD\x31\x82", 20); + + talloc_free(content); +} + +void test_sss_cert_get_content_2(void **state) +{ + int ret; + struct sss_cert_content *content; + struct san_list *i; + + ret = sss_cert_get_content(NULL, test_cert2_der, sizeof(test_cert2_der), + &content); + assert_int_equal(ret, 0); + assert_non_null(content); + assert_non_null(content->issuer_str); + assert_string_equal(content->issuer_str, + "CN=ad-AD-SERVER-CA,DC=ad,DC=devel"); + assert_non_null(content->subject_str); +#if 0 +FIXME: + assert_string_equal(content->subject_str, + "E=test.user@email.domain,CN=t u,CN=Users,DC=ad,DC=devel,DC=ad,DC=devel"); + //"CN=t u/emailAddress=test.user@email.domain,DC=ad,DC=devel"); +#endif + assert_int_equal(content->key_usage, SSS_KU_DIGITAL_SIGNATURE + |SSS_KU_KEY_ENCIPHERMENT); + assert_non_null(content->extended_key_usage_oids); + assert_non_null(content->extended_key_usage_oids[0]); + assert_true(string_in_list("1.3.6.1.5.5.7.3.2", + discard_const(content->extended_key_usage_oids), true)); + assert_true(string_in_list("1.3.6.1.5.5.7.3.4", + discard_const(content->extended_key_usage_oids), true)); + /* Can use Microsoft Encrypted File System OID */ + assert_true(string_in_list("1.3.6.1.4.1.311.10.3.4", + discard_const(content->extended_key_usage_oids), true)); + assert_null(content->extended_key_usage_oids[3]); + assert_int_equal(content->cert_der_size, sizeof(test_cert2_der)); + assert_memory_equal(content->cert_der, test_cert2_der, + sizeof(test_cert2_der)); + + assert_non_null(content->issuer_rdn_list); + assert_string_equal(content->issuer_rdn_list[0], "DC=devel"); + assert_string_equal(content->issuer_rdn_list[1], "DC=ad"); + assert_string_equal(content->issuer_rdn_list[2], "CN=ad-AD-SERVER-CA"); + assert_null(content->issuer_rdn_list[3]); + + assert_non_null(content->subject_rdn_list); + assert_string_equal(content->subject_rdn_list[0], "DC=devel"); + assert_string_equal(content->subject_rdn_list[1], "DC=ad"); + assert_string_equal(content->subject_rdn_list[2], "CN=Users"); + assert_string_equal(content->subject_rdn_list[3], "CN=t u"); + assert_string_equal(content->subject_rdn_list[4], + "E=test.user@email.domain"); + assert_null(content->subject_rdn_list[5]); + + assert_non_null(content->san_list); + + DLIST_FOR_EACH(i, content->san_list) { + switch (i->san_opt) { + case SAN_RFC822_NAME: + assert_string_equal(i->val, "test.user@email.domain"); + assert_string_equal(i->short_name, "test.user"); + break; + case SAN_STRING_OTHER_NAME: + assert_string_equal(i->other_name_oid, "1.3.6.1.4.1.311.20.2.3"); + assert_int_equal(i->bin_val_len, 14); + assert_memory_equal(i->bin_val, "\f\ftu1@ad.devel", 14); + break; + case SAN_NT: + case SAN_PRINCIPAL: + assert_string_equal(i->val, "tu1@ad.devel"); + assert_string_equal(i->short_name, "tu1"); + break; + default: + assert_true(false); + } + } + + assert_int_equal(content->serial_number_size, 10); + assert_non_null(content->serial_number); + assert_memory_equal(content->serial_number, "\x61\x22\x88\xc2\x00\x00\x00\x00\x02\xa6", 10); + assert_string_equal(content->serial_number_dec_str, "458706592575796350550694"); + + assert_int_equal(content->subject_key_id_size, 20); + assert_non_null(content->subject_key_id); + assert_memory_equal(content->subject_key_id, "\x49\xAC\xAD\xE0\x65\x30\xC4\xCE\xA0\x09\x03\x5B\xAD\x4A\x7B\x49\x5E\xC9\x6C\xB4", 20); + + talloc_free(content); +} + +void test_sss_cert_get_content_test_cert_0003(void **state) +{ + int ret; + uint8_t *der; + size_t der_size; + struct sss_cert_content *content; + + der = sss_base64_decode(NULL, SSSD_TEST_CERT_0003, &der_size); + assert_non_null(der); + + ret = sss_cert_get_content(NULL, der, der_size, &content); + talloc_free(der); + assert_int_equal(ret, 0); + assert_non_null(content); + assert_non_null(content->issuer_str); + assert_string_equal(content->issuer_str, + "CN=SSSD test CA,OU=SSSD test,O=SSSD"); + + assert_non_null(content->issuer_rdn_list); + assert_string_equal(content->issuer_rdn_list[0], "O=SSSD"); + assert_string_equal(content->issuer_rdn_list[1], "OU=SSSD test"); + assert_string_equal(content->issuer_rdn_list[2], "CN=SSSD test CA"); + assert_null(content->issuer_rdn_list[3]); + + assert_non_null(content->subject_str); + assert_string_equal(content->subject_str, + "CN=SSSD test cert 0003,OU=SSSD test,O=SSSD"); + + assert_non_null(content->subject_rdn_list); + assert_string_equal(content->issuer_rdn_list[0], "O=SSSD"); + assert_string_equal(content->issuer_rdn_list[1], "OU=SSSD test"); + assert_string_equal(content->subject_rdn_list[2], "CN=SSSD test cert 0003"); + assert_null(content->subject_rdn_list[3]); + + assert_int_equal(content->key_usage, SSS_KU_DIGITAL_SIGNATURE + |SSS_KU_KEY_ENCIPHERMENT); + + assert_non_null(content->extended_key_usage_oids); + assert_null(content->extended_key_usage_oids[0]); + + assert_null(content->san_list); + + assert_int_equal(content->serial_number_size, 1); + assert_non_null(content->serial_number); + assert_memory_equal(content->serial_number, SSSD_TEST_CERT_SERIAL_0003, 1); + assert_string_equal(content->serial_number_dec_str, SSSD_TEST_CERT_DEC_SERIAL_0003); + + assert_int_equal(content->subject_key_id_size, 20); + assert_non_null(content->subject_key_id); + assert_memory_equal(content->subject_key_id, "\x28\x3E\xBB\xD6\xD9\x5C\xFE\xC1\xFB\x7C\x49\x3B\x19\xB4\xD6\x63\xB2\x44\x8C\x41", 20); + + talloc_free(content); +} + +void test_sss_cert_get_content_test_cert_0004(void **state) +{ + int ret; + uint8_t *der; + size_t der_size; + struct sss_cert_content *content; + + der = sss_base64_decode(NULL, SSSD_TEST_CERT_0004, &der_size); + assert_non_null(der); + + ret = sss_cert_get_content(NULL, der, der_size, &content); + talloc_free(der); + assert_int_equal(ret, 0); + assert_non_null(content); + assert_non_null(content->issuer_str); + assert_string_equal(content->issuer_str, + "CN=SSSD test CA,OU=SSSD test,O=SSSD"); + + assert_non_null(content->issuer_rdn_list); + assert_string_equal(content->issuer_rdn_list[0], "O=SSSD"); + assert_string_equal(content->issuer_rdn_list[1], "OU=SSSD test"); + assert_string_equal(content->issuer_rdn_list[2], "CN=SSSD test CA"); + assert_null(content->issuer_rdn_list[3]); + + assert_non_null(content->subject_str); + assert_string_equal(content->subject_str, + "CN=SSSD test cert 0004,OU=SSSD test,O=SSSD"); + + assert_non_null(content->subject_rdn_list); + assert_string_equal(content->issuer_rdn_list[0], "O=SSSD"); + assert_string_equal(content->issuer_rdn_list[1], "OU=SSSD test"); + assert_string_equal(content->subject_rdn_list[2], "CN=SSSD test cert 0004"); + assert_null(content->subject_rdn_list[3]); + + assert_int_equal(content->key_usage, UINT32_MAX); + + assert_non_null(content->extended_key_usage_oids); + assert_null(content->extended_key_usage_oids[0]); + + assert_null(content->san_list); + + assert_int_equal(content->serial_number_size, 1); + assert_non_null(content->serial_number); + assert_memory_equal(content->serial_number, SSSD_TEST_CERT_SERIAL_0004, 1); + assert_string_equal(content->serial_number_dec_str, SSSD_TEST_CERT_DEC_SERIAL_0004); + + assert_int_equal(content->subject_key_id_size, 20); + assert_non_null(content->subject_key_id); + assert_memory_equal(content->subject_key_id, "\xDD\x09\x78\x8E\xE6\x50\xB3\xE3\x3B\x0D\xFB\x9F\xCB\x6D\x66\x48\x95\x1D\xAA\x52", 20); + + talloc_free(content); +} + +void test_sss_cert_get_content_test_cert_0001(void **state) +{ + int ret; + uint8_t *der; + size_t der_size; + struct sss_cert_content *content; + struct san_list *i; + uint32_t check = 0; + + der = sss_base64_decode(NULL, SSSD_TEST_CERT_0001, &der_size); + assert_non_null(der); + + ret = sss_cert_get_content(NULL, der, der_size, &content); + talloc_free(der); + assert_int_equal(ret, 0); + assert_non_null(content); + + assert_non_null(content->san_list); + DLIST_FOR_EACH(i, content->san_list) { + switch (i->san_opt) { + case SAN_RFC822_NAME: + assert_string_equal(i->val, "sssd-devel@lists.fedorahosted.org"); + assert_string_equal(i->short_name, "sssd-devel"); + check |= 1; + break; + case SAN_URI: + assert_string_equal(i->val, "https://github.com/SSSD/sssd//"); + check |= 2; + break; + default: + assert_true(false); + } + } + assert_int_equal(check, 3); + + talloc_free(content); +} + +void test_sss_cert_get_content_test_cert_with_sid_ext(void **state) +{ + int ret; + uint8_t *der; + size_t der_size; + struct sss_cert_content *content; + + der = sss_base64_decode(NULL, TEST_CERT_WITH_SID_EXT, &der_size); + assert_non_null(der); + + ret = sss_cert_get_content(NULL, der, der_size, &content); + talloc_free(der); + assert_int_equal(ret, 0); + assert_non_null(content); + + assert_non_null(content->sid_ext); + assert_string_equal(content->sid_ext, "S-1-5-21-294026126-3378245018-1203103949-1001"); + talloc_free(content); +} + +static void test_sss_certmap_match_cert(void **state) +{ + struct sss_certmap_ctx *ctx; + int ret; + size_t c; + + struct match_tests { + const char *rule; + int result; + } match_tests[] = { + {"KRB5:<KU>digitalSignature", 0}, + {"KRB5:<KU>digitalSignature,nonRepudiation", 0}, + {"KRB5:<KU>digitalSignature,cRLSign", ENOENT}, + {"KRB5:<EKU>clientAuth", 0}, + {"KRB5:<EKU>clientAuth,OCSPSigning", ENOENT}, + {"KRB5:<EKU>clientAuth,serverAuth", 0}, + {NULL, 0} + }; + + struct match_tests match_tests_2[] = { + {"KRB5:<KU>digitalSignature", 0}, + {"KRB5:<KU>keyEncipherment", 0}, + {"KRB5:<KU>digitalSignature,keyEncipherment", 0}, + {"KRB5:<KU>digitalSignature,keyEncipherment,cRLSign", ENOENT}, + {"KRB5:<EKU>clientAuth", 0}, + {"KRB5:<EKU>clientAuth,1.3.6.1.4.1.311.10.3.4", 0}, + {"KRB5:<EKU>clientAuth,1.3.6.1.4.1.311.10.3.41", ENOENT}, + {"KRB5:<SAN>tu1", 0}, + {"KRB5:<SAN:Principal>tu1", 0}, + {"KRB5:<SAN:ntPrincipalName>tu1", 0}, + {"KRB5:<SAN:pkinitSAN>tu1", ENOENT}, + {"KRB5:<SAN:Principal>^tu1@ad.devel$", 0}, + {"KRB5:<SAN:rfc822Name>tu", ENOENT}, + {"KRB5:<SAN:rfc822Name>test.user", 0}, + {"KRB5:<SAN:rfc822Name>test.user<SAN>tu1", 0}, + {"KRB5:||<SAN:rfc822Name>test.user<SAN>tu1", 0}, + {"KRB5:&&<SAN:rfc822Name>tu1<SAN>tu1", ENOENT}, + {"KRB5:||<SAN:rfc822Name>tu1<SAN>tu1", 0}, + {"KRB5:<SAN:otherName>MTIz", ENOENT}, /* 123 */ + {"KRB5:<SAN:otherName>DAx0dTFAYWQuZGV2ZWw=", 0}, /* "\f\ftu1@ad.devel" */ + {"KRB5:<SAN:otherName>DAx0dTFAYWQuZGV2ZWx4", ENOENT}, /* "\f\ftu1@ad.develx" */ + {"KRB5:<SAN:otherName>dHUxQGFkLmRldmVs", 0}, /* "tu1@ad.devel" */ + {"KRB5:<SAN:1.3.6.1.4.1.311.20.2.3>test", ENOENT}, + {"KRB5:<SAN:1.3.6.1.4.1.311.20.2.3>tu1@ad", 0}, + /* Fails because the NT principal SAN starts with binary values */ + {"KRB5:<SAN:1.3.6.1.4.1.311.20.2.3>^tu1@ad.devel$", ENOENT}, + {NULL, 0} + }; + + struct match_tests match_tests_3[] = { + {"KRB5:<KU>digitalSignature", 0}, + {"KRB5:<KU>keyEncipherment", 0}, + {"KRB5:<KU>keyAgreement", 0}, + {"KRB5:<KU>digitalSignature,keyAgreement,keyEncipherment", 0}, + {"KRB5:<SAN:Principal>test", 0}, + {"KRB5:<SAN:ntPrincipal>test", ENOENT}, + {"KRB5:<SAN:Principal>comp2", 0}, + {"KRB5:<SAN:Principal>another", 0}, + {"KRB5:<SAN:Principal>test/comp2/another@SSSD.TEST", 0}, + {"KRB5:<SAN:Principal>^test/comp2/another@SSSD.TEST$", 0}, + {"KRB5:<SAN:pkinitSAN>^test/comp2/another@SSSD.TEST$", 0}, + {NULL, 0} + }; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, "KRB5:<ISSUER>xyz<SUBJECT>xyz", + NULL, NULL); + assert_int_equal(ret, EOK); + + ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der), + sizeof(test_cert_der)); + assert_int_equal(ret, ENOENT); + + ret = sss_certmap_add_rule(ctx, 1, + "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL", + NULL, NULL); + assert_int_equal(ret, EOK); + + ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der), + sizeof(test_cert_der)); + assert_int_equal(ret, 0); + + sss_certmap_free_ctx(ctx); + + for (c = 0; match_tests[c].rule != NULL; c++) { + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, match_tests[c].rule, NULL, NULL); + assert_int_equal(ret, EOK); + + ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der), + sizeof(test_cert_der)); + assert_int_equal(ret, match_tests[c].result); + + sss_certmap_free_ctx(ctx); + } + + for (c = 0; match_tests_2[c].rule != NULL; c++) { + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + print_error("Checking matching rule [%s]\n", match_tests_2[c].rule); + + ret = sss_certmap_add_rule(ctx, 1, match_tests_2[c].rule, NULL, NULL); + assert_int_equal(ret, EOK); + + ret = sss_certmap_match_cert(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der)); + assert_int_equal(ret, match_tests_2[c].result); + + sss_certmap_free_ctx(ctx); + } + + for (c = 0; match_tests_3[c].rule != NULL; c++) { + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + print_error("Checking matching rule [%s]\n", match_tests_3[c].rule); + + ret = sss_certmap_add_rule(ctx, 1, match_tests_3[c].rule, NULL, NULL); + assert_int_equal(ret, EOK); + + ret = sss_certmap_match_cert(ctx, discard_const(test_cert3_der), + sizeof(test_cert3_der)); + assert_int_equal(ret, match_tests_3[c].result); + + sss_certmap_free_ctx(ctx); + } +} + +static void test_sss_certmap_add_mapping_rule(void **state) +{ + struct sss_certmap_ctx *ctx; + int ret; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 1, NULL, "FWEAWEF:fwefwe", NULL); + assert_int_equal(ret, ESRCH); + + ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:abc", NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule); + assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list); + assert_int_equal(comp_string, + ctx->prio_list->rule_list->parsed_mapping_rule->list->type); + assert_string_equal("abc", + ctx->prio_list->rule_list->parsed_mapping_rule->list->val); + talloc_free(ctx); + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:abc{issuer_dn}", NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule); + assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list); + assert_int_equal(comp_string, + ctx->prio_list->rule_list->parsed_mapping_rule->list->type); + assert_string_equal("abc", + ctx->prio_list->rule_list->parsed_mapping_rule->list->val); + assert_int_equal(comp_template, + ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type); + assert_string_equal("issuer_dn", + ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val); + talloc_free(ctx); + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + ret = sss_certmap_add_rule(ctx, 1, NULL, "{issuer_dn}a:b{{c}}", NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule); + assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list); + assert_int_equal(comp_template, + ctx->prio_list->rule_list->parsed_mapping_rule->list->type); + assert_string_equal("issuer_dn", + ctx->prio_list->rule_list->parsed_mapping_rule->list->val); + assert_int_equal(comp_string, + ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type); + assert_string_equal("a:b{c}", + ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val); + talloc_free(ctx); + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:{issuer_dn}{subject_dn}", + NULL); + assert_int_equal(ret, 0); + assert_non_null(ctx->prio_list); + assert_non_null(ctx->prio_list->rule_list); + assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule); + assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list); + assert_int_equal(comp_template, + ctx->prio_list->rule_list->parsed_mapping_rule->list->type); + assert_string_equal("issuer_dn", + ctx->prio_list->rule_list->parsed_mapping_rule->list->val); + assert_int_equal(comp_template, + ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type); + assert_string_equal("subject_dn", + ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val); + talloc_free(ctx); +} + +#define TEST_CERT_BIN \ + "\\30\\82\\04\\09\\30\\82\\02\\f1\\a0\\03\\02\\01\\02\\02\\01\\09" \ + "\\30\\0d\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01\\01\\0b\\05\\00\\30" \ + "\\34\\31\\12\\30\\10\\06\\03\\55\\04\\0a\\0c\\09\\49\\50\\41\\2e" \ + "\\44\\45\\56\\45\\4c\\31\\1e\\30\\1c\\06\\03\\55\\04\\03\\0c\\15" \ + "\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65\\20\\41\\75\\74\\68" \ + "\\6f\\72\\69\\74\\79\\30\\1e\\17\\0d\\31\\35\\30\\34\\32\\38\\31" \ + "\\30\\32\\31\\31\\31\\5a\\17\\0d\\31\\37\\30\\34\\32\\38\\31\\30" \ + "\\32\\31\\31\\31\\5a\\30\\32\\31\\12\\30\\10\\06\\03\\55\\04\\0a" \ + "\\0c\\09\\49\\50\\41\\2e\\44\\45\\56\\45\\4c\\31\\1c\\30\\1a\\06" \ + "\\03\\55\\04\\03\\0c\\13\\69\\70\\61\\2d\\64\\65\\76\\65\\6c\\2e" \ + "\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\30\\82\\01\\22\\30\\0d\\06" \ + "\\09\\2a\\86\\48\\86\\f7\\0d\\01\\01\\01\\05\\00\\03\\82\\01\\0f" \ + "\\00\\30\\82\\01\\0a\\02\\82\\01\\01\\00\\b2\\32\\92\\ab\\47\\b8" \ + "\\0c\\13\\54\\4a\\1f\\1e\\29\\06\\ff\\d0\\50\\cb\\f7\\5f\\79\\91" \ + "\\65\\b1\\39\\01\\83\\6a\\ad\\9e\\77\\3b\\f3\\0d\\d7\\b9\\f6\\dc" \ + "\\9e\\4a\\49\\a7\\d0\\66\\72\\cc\\bf\\77\\d6\\de\\a9\\fe\\67\\96" \ + "\\cc\\49\\f1\\37\\23\\2e\\c4\\50\\f4\\eb\\ba\\62\\d4\\23\\4d\\f3" \ + "\\37\\38\\82\\ee\\3b\\3f\\2c\\d0\\80\\9b\\17\\aa\\9b\\eb\\a6\\dd" \ + "\\f6\\15\\ff\\06\\b2\\ce\\ff\\df\\8a\\9e\\95\\85\\49\\1f\\84\\fd" \ + "\\81\\26\\ce\\06\\32\\0d\\36\\ca\\7c\\15\\81\\68\\6b\\8f\\3e\\b3" \ + "\\a2\\fc\\ae\\af\\c2\\44\\58\\15\\95\\40\\fc\\56\\19\\91\\80\\ed" \ + "\\42\\11\\66\\04\\ef\\3c\\e0\\76\\33\\4b\\83\\fa\\7e\\b4\\47\\dc" \ + "\\fb\\ed\\46\\a5\\8d\\0a\\66\\87\\a5\\ef\\7b\\74\\62\\ac\\be\\73" \ + "\\36\\c9\\b4\\fe\\20\\c4\\81\\f3\\fe\\78\\19\\a8\\d0\\af\\7f\\81" \ + "\\72\\24\\61\\d9\\76\\93\\e3\\0b\\d2\\4f\\19\\17\\33\\57\\d4\\82" \ + "\\b0\\f1\\a8\\03\\f6\\01\\99\\a9\\b8\\8c\\83\\c9\\ba\\19\\87\\ea" \ + "\\d6\\3b\\06\\eb\\4c\\f7\\f1\\e5\\28\\a9\\10\\b6\\46\\de\\e1\\e1" \ + "\\3f\\c1\\cc\\72\\be\\2a\\43\\c6\\f6\\d0\\b5\\a0\\c4\\24\\6e\\4f" \ + "\\bd\\ec\\22\\8a\\07\\11\\3d\\f9\\d3\\15\\02\\03\\01\\00\\01\\a3" \ + "\\82\\01\\26\\30\\82\\01\\22\\30\\1f\\06\\03\\55\\1d\\23\\04\\18" \ + "\\30\\16\\80\\14\\f2\\9d\\42\\4e\\0f\\c4\\48\\25\\58\\2f\\1c\\ce" \ + "\\0f\\a1\\3f\\22\\c8\\55\\c8\\91\\30\\3b\\06\\08\\2b\\06\\01\\05" \ + "\\05\\07\\01\\01\\04\\2f\\30\\2d\\30\\2b\\06\\08\\2b\\06\\01\\05" \ + "\\05\\07\\30\\01\\86\\1f\\68\\74\\74\\70\\3a\\2f\\2f\\69\\70\\61" \ + "\\2d\\63\\61\\2e\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\2f\\63\\61" \ + "\\2f\\6f\\63\\73\\70\\30\\0e\\06\\03\\55\\1d\\0f\\01\\01\\ff\\04" \ + "\\04\\03\\02\\04\\f0\\30\\1d\\06\\03\\55\\1d\\25\\04\\16\\30\\14" \ + "\\06\\08\\2b\\06\\01\\05\\05\\07\\03\\01\\06\\08\\2b\\06\\01\\05" \ + "\\05\\07\\03\\02\\30\\74\\06\\03\\55\\1d\\1f\\04\\6d\\30\\6b\\30" \ + "\\69\\a0\\31\\a0\\2f\\86\\2d\\68\\74\\74\\70\\3a\\2f\\2f\\69\\70" \ + "\\61\\2d\\63\\61\\2e\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\2f\\69" \ + "\\70\\61\\2f\\63\\72\\6c\\2f\\4d\\61\\73\\74\\65\\72\\43\\52\\4c" \ + "\\2e\\62\\69\\6e\\a2\\34\\a4\\32\\30\\30\\31\\0e\\30\\0c\\06\\03" \ + "\\55\\04\\0a\\0c\\05\\69\\70\\61\\63\\61\\31\\1e\\30\\1c\\06\\03" \ + "\\55\\04\\03\\0c\\15\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65" \ + "\\20\\41\\75\\74\\68\\6f\\72\\69\\74\\79\\30\\1d\\06\\03\\55\\1d" \ + "\\0e\\04\\16\\04\\14\\2d\\2b\\3f\\cb\\f5\\b2\\ff\\32\\2c\\a8\\c2" \ + "\\1c\\dd\\bd\\8c\\80\\1e\\dd\\31\\82\\30\\0d\\06\\09\\2a\\86\\48" \ + "\\86\\f7\\0d\\01\\01\\0b\\05\\00\\03\\82\\01\\01\\00\\9a\\47\\2e" \ + "\\50\\a7\\4d\\1d\\53\\0f\\c9\\71\\42\\0c\\e5\\da\\7d\\49\\64\\e7" \ + "\\ab\\c8\\df\\df\\02\\c1\\87\\d1\\5b\\de\\da\\6f\\2b\\e4\\f0\\be" \ + "\\ba\\09\\df\\02\\85\\0b\\8a\\e6\\9b\\06\\7d\\69\\38\\6c\\72\\ff" \ + "\\4c\\7b\\2a\\0d\\3f\\23\\2f\\16\\46\\ff\\05\\93\\b0\\ea\\24\\28" \ + "\\d7\\12\\a1\\57\\b8\\59\\19\\25\\f3\\43\\0a\\d3\\fd\\0f\\37\\8d" \ + "\\b8\\ca\\15\\e7\\48\\8a\\a0\\c7\\c7\\4b\\7f\\01\\3c\\58\\d7\\37" \ + "\\e5\\ff\\7d\\2b\\01\\ac\\0d\\9f\\51\\6a\\e5\\40\\24\\e6\\5e\\55" \ + "\\0d\\f7\\b8\\2f\\42\\ac\\6d\\e5\\29\\6b\\c6\\0b\\a4\\bf\\19\\bd" \ + "\\39\\27\\ee\\fe\\c5\\b3\\db\\62\\d4\\be\\d2\\47\\ba\\96\\30\\5a" \ + "\\fd\\62\\00\\b8\\27\\5d\\2f\\3a\\94\\0b\\95\\35\\85\\40\\2c\\bc" \ + "\\67\\df\\8a\\f9\\f1\\7b\\19\\96\\3e\\42\\48\\13\\23\\04\\95\\a9" \ + "\\6b\\11\\33\\81\\47\\5a\\83\\72\\f6\\20\\fa\\8e\\41\\7b\\8f\\77" \ + "\\47\\7c\\c7\\5d\\46\\f4\\4f\\fd\\81\\0a\\ae\\39\\27\\b6\\6a\\26" \ + "\\63\\b1\\d3\\bf\\55\\83\\82\\9b\\36\\6c\\33\\64\\0f\\50\\c0\\55" \ + "\\94\\13\\c3\\85\\f4\\d5\\71\\65\\d0\\c0\\dd\\fc\\e6\\ec\\9c\\5b" \ + "\\f0\\11\\b5\\2c\\f3\\48\\c1\\36\\8c\\a2\\96\\48\\84" + +#define TEST_CERT2_BIN \ + "\\30\\82\\06\\98\\30\\82\\05\\80\\a0\\03\\02\\01\\02\\02\\0a\\61" \ + "\\22\\88\\c2\\00\\00\\00\\00\\02\\a6\\30\\0d\\06\\09\\2a\\86\\48" \ + "\\86\\f7\\0d\\01\\01\\05\\05\\00\\30\\45\\31\\15\\30\\13\\06\\0a" \ + "\\09\\92\\26\\89\\93\\f2\\2c\\64\\01\\19\\16\\05\\64\\65\\76\\65" \ + "\\6c\\31\\12\\30\\10\\06\\0a\\09\\92\\26\\89\\93\\f2\\2c\\64\\01" \ + "\\19\\16\\02\\61\\64\\31\\18\\30\\16\\06\\03\\55\\04\\03\\13\\0f" \ + "\\61\\64\\2d\\41\\44\\2d\\53\\45\\52\\56\\45\\52\\2d\\43\\41\\30" \ + "\\1e\\17\\0d\\31\\36\\31\\31\\31\\31\\31\\33\\35\\31\\31\\31\\5a" \ + "\\17\\0d\\31\\37\\31\\31\\31\\31\\31\\33\\35\\31\\31\\31\\5a\\30" \ + "\\70\\31\\15\\30\\13\\06\\0a\\09\\92\\26\\89\\93\\f2\\2c\\64\\01" \ + "\\19\\16\\05\\64\\65\\76\\65\\6c\\31\\12\\30\\10\\06\\0a\\09\\92" \ + "\\26\\89\\93\\f2\\2c\\64\\01\\19\\16\\02\\61\\64\\31\\0e\\30\\0c" \ + "\\06\\03\\55\\04\\03\\13\\05\\55\\73\\65\\72\\73\\31\\0c\\30\\0a" \ + "\\06\\03\\55\\04\\03\\13\\03\\74\\20\\75\\31\\25\\30\\23\\06\\09" \ + "\\2a\\86\\48\\86\\f7\\0d\\01\\09\\01\\16\\16\\74\\65\\73\\74\\2e" \ + "\\75\\73\\65\\72\\40\\65\\6d\\61\\69\\6c\\2e\\64\\6f\\6d\\61\\69" \ + "\\6e\\30\\82\\01\\22\\30\\0d\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01" \ + "\\01\\01\\05\\00\\03\\82\\01\\0f\\00\\30\\82\\01\\0a\\02\\82\\01" \ + "\\01\\00\\9c\\cf\\36\\99\\de\\63\\74\\2b\\77\\25\\9e\\24\\d9\\77" \ + "\\4b\\5f\\98\\c0\\8c\\d7\\20\\91\\c0\\1c\\e8\\37\\45\\bf\\3c\\d9" \ + "\\33\\bd\\e9\\de\\c9\\5d\\d4\\cd\\06\\0a\\0d\\d4\\f1\\7c\\74\\5b" \ + "\\29\\d5\\66\\9c\\2c\\9f\\6b\\1a\\0f\\0d\\e6\\6c\\62\\a5\\41\\4f" \ + "\\c3\\a4\\88\\27\\11\\5d\\b7\\b1\\fb\\f8\\8d\\ee\\43\\8d\\93\\b5" \ + "\\8c\\b4\\34\\06\\f5\\e9\\2f\\5a\\26\\68\\d7\\43\\60\\82\\5e\\22" \ + "\\a7\\c6\\34\\40\\19\\a5\\8e\\f0\\58\\9f\\16\\2d\\43\\3f\\0c\\da" \ + "\\e2\\23\\f6\\09\\2a\\5e\\bd\\84\\27\\c8\\ab\\d5\\70\\f8\\3d\\9c" \ + "\\14\\c2\\c2\\a2\\77\\e8\\44\\73\\10\\01\\34\\40\\1f\\c6\\2f\\a0" \ + "\\70\\ee\\2f\\d5\\4b\\be\\4c\\c7\\45\\f7\\ac\\9c\\c3\\68\\5b\\1d" \ + "\\5a\\4b\\77\\65\\76\\e4\\b3\\92\\f4\\84\\0a\\9e\\6a\\9c\\c9\\53" \ + "\\42\\9f\\6d\\fe\\f9\\f5\\f2\\9a\\15\\50\\47\\ef\\f4\\06\\59\\c8" \ + "\\50\\48\\4b\\46\\95\\68\\25\\c5\\bd\\4f\\65\\34\\00\\fc\\31\\69" \ + "\\f8\\3e\\e0\\20\\83\\41\\27\\0b\\5c\\46\\98\\14\\f0\\07\\de\\02" \ + "\\17\\b1\\d2\\9c\\be\\1c\\0d\\56\\22\\1b\\02\\fe\\da\\69\\b9\\ef" \ + "\\91\\37\\39\\7f\\24\\da\\c4\\81\\5e\\82\\31\\2f\\98\\1d\\f7\\73" \ + "\\5b\\23\\02\\03\\01\\00\\01\\a3\\82\\03\\5d\\30\\82\\03\\59\\30" \ + "\\3d\\06\\09\\2b\\06\\01\\04\\01\\82\\37\\15\\07\\04\\30\\30\\2e" \ + "\\06\\26\\2b\\06\\01\\04\\01\\82\\37\\15\\08\\87\\85\\a1\\23\\84" \ + "\\c8\\b2\\26\\83\\9d\\9d\\21\\82\\d4\\a6\\1b\\86\\a3\\ba\\37\\81" \ + "\\10\\85\\89\\d5\\02\\d6\\8f\\24\\02\\01\\64\\02\\01\\02\\30\\29" \ + "\\06\\03\\55\\1d\\25\\04\\22\\30\\20\\06\\08\\2b\\06\\01\\05\\05" \ + "\\07\\03\\02\\06\\08\\2b\\06\\01\\05\\05\\07\\03\\04\\06\\0a\\2b" \ + "\\06\\01\\04\\01\\82\\37\\0a\\03\\04\\30\\0e\\06\\03\\55\\1d\\0f" \ + "\\01\\01\\ff\\04\\04\\03\\02\\05\\a0\\30\\35\\06\\09\\2b\\06\\01" \ + "\\04\\01\\82\\37\\15\\0a\\04\\28\\30\\26\\30\\0a\\06\\08\\2b\\06" \ + "\\01\\05\\05\\07\\03\\02\\30\\0a\\06\\08\\2b\\06\\01\\05\\05\\07" \ + "\\03\\04\\30\\0c\\06\\0a\\2b\\06\\01\\04\\01\\82\\37\\0a\\03\\04" \ + "\\30\\81\\94\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01\\09\\0f\\04\\81" \ + "\\86\\30\\81\\83\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01" \ + "\\2a\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01\\2d\\30\\0b" \ + "\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01\\16\\30\\0b\\06\\09\\60" \ + "\\86\\48\\01\\65\\03\\04\\01\\19\\30\\0b\\06\\09\\60\\86\\48\\01" \ + "\\65\\03\\04\\01\\02\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04" \ + "\\01\\05\\30\\0a\\06\\08\\2a\\86\\48\\86\\f7\\0d\\03\\07\\30\\07" \ + "\\06\\05\\2b\\0e\\03\\02\\07\\30\\0e\\06\\08\\2a\\86\\48\\86\\f7" \ + "\\0d\\03\\02\\02\\02\\00\\80\\30\\0e\\06\\08\\2a\\86\\48\\86\\f7" \ + "\\0d\\03\\04\\02\\02\\02\\00\\30\\1d\\06\\03\\55\\1d\\0e\\04\\16" \ + "\\04\\14\\49\\ac\\ad\\e0\\65\\30\\c4\\ce\\a0\\09\\03\\5b\\ad\\4a" \ + "\\7b\\49\\5e\\c9\\6c\\b4\\30\\1f\\06\\03\\55\\1d\\23\\04\\18\\30" \ + "\\16\\80\\14\\62\\50\\b6\\8d\\a1\\e6\\2d\\91\\bf\\b0\\54\\4d\\8f" \ + "\\a8\\ca\\10\\ae\\b8\\dd\\54\\30\\81\\cc\\06\\03\\55\\1d\\1f\\04" \ + "\\81\\c4\\30\\81\\c1\\30\\81\\be\\a0\\81\\bb\\a0\\81\\b8\\86\\81" \ + "\\b5\\6c\\64\\61\\70\\3a\\2f\\2f\\2f\\43\\4e\\3d\\61\\64\\2d\\41" \ + "\\44\\2d\\53\\45\\52\\56\\45\\52\\2d\\43\\41\\2c\\43\\4e\\3d\\61" \ + "\\64\\2d\\73\\65\\72\\76\\65\\72\\2c\\43\\4e\\3d\\43\\44\\50\\2c" \ + "\\43\\4e\\3d\\50\\75\\62\\6c\\69\\63\\25\\32\\30\\4b\\65\\79\\25" \ + "\\32\\30\\53\\65\\72\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\53\\65" \ + "\\72\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\43\\6f\\6e\\66\\69\\67" \ + "\\75\\72\\61\\74\\69\\6f\\6e\\2c\\44\\43\\3d\\61\\64\\2c\\44\\43" \ + "\\3d\\64\\65\\76\\65\\6c\\3f\\63\\65\\72\\74\\69\\66\\69\\63\\61" \ + "\\74\\65\\52\\65\\76\\6f\\63\\61\\74\\69\\6f\\6e\\4c\\69\\73\\74" \ + "\\3f\\62\\61\\73\\65\\3f\\6f\\62\\6a\\65\\63\\74\\43\\6c\\61\\73" \ + "\\73\\3d\\63\\52\\4c\\44\\69\\73\\74\\72\\69\\62\\75\\74\\69\\6f" \ + "\\6e\\50\\6f\\69\\6e\\74\\30\\81\\be\\06\\08\\2b\\06\\01\\05\\05" \ + "\\07\\01\\01\\04\\81\\b1\\30\\81\\ae\\30\\81\\ab\\06\\08\\2b\\06" \ + "\\01\\05\\05\\07\\30\\02\\86\\81\\9e\\6c\\64\\61\\70\\3a\\2f\\2f" \ + "\\2f\\43\\4e\\3d\\61\\64\\2d\\41\\44\\2d\\53\\45\\52\\56\\45\\52" \ + "\\2d\\43\\41\\2c\\43\\4e\\3d\\41\\49\\41\\2c\\43\\4e\\3d\\50\\75" \ + "\\62\\6c\\69\\63\\25\\32\\30\\4b\\65\\79\\25\\32\\30\\53\\65\\72" \ + "\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\53\\65\\72\\76\\69\\63\\65" \ + "\\73\\2c\\43\\4e\\3d\\43\\6f\\6e\\66\\69\\67\\75\\72\\61\\74\\69" \ + "\\6f\\6e\\2c\\44\\43\\3d\\61\\64\\2c\\44\\43\\3d\\64\\65\\76\\65" \ + "\\6c\\3f\\63\\41\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65\\3f" \ + "\\62\\61\\73\\65\\3f\\6f\\62\\6a\\65\\63\\74\\43\\6c\\61\\73\\73" \ + "\\3d\\63\\65\\72\\74\\69\\66\\69\\63\\61\\74\\69\\6f\\6e\\41\\75" \ + "\\74\\68\\6f\\72\\69\\74\\79\\30\\3f\\06\\03\\55\\1d\\11\\04\\38" \ + "\\30\\36\\a0\\1c\\06\\0a\\2b\\06\\01\\04\\01\\82\\37\\14\\02\\03" \ + "\\a0\\0e\\0c\\0c\\74\\75\\31\\40\\61\\64\\2e\\64\\65\\76\\65\\6c" \ + "\\81\\16\\74\\65\\73\\74\\2e\\75\\73\\65\\72\\40\\65\\6d\\61\\69" \ + "\\6c\\2e\\64\\6f\\6d\\61\\69\\6e\\30\\0d\\06\\09\\2a\\86\\48\\86" \ + "\\f7\\0d\\01\\01\\05\\05\\00\\03\\82\\01\\01\\00\\41\\45\\0a\\6d" \ + "\\bb\\7f\\5c\\07\\0c\\c9\\b0\\39\\55\\6d\\7c\\b5\\02\\cd\\e8\\b2" \ + "\\e5\\02\\94\\77\\60\\db\\d1\\af\\1d\\db\\44\\5f\\ce\\83\\db\\80" \ + "\\2e\\e2\\b2\\08\\25\\82\\14\\cb\\48\\95\\20\\13\\6c\\a9\\aa\\f8" \ + "\\31\\56\\ed\\c0\\3b\\d4\\ae\\2e\\e3\\8f\\05\\fc\\ab\\5f\\2a\\69" \ + "\\23\\bc\\b8\\8c\\ec\\2d\\a9\\0b\\86\\95\\73\\73\\db\\17\\ce\\c6" \ + "\\ae\\c5\\b4\\c1\\25\\87\\3b\\67\\43\\9e\\87\\5a\\e6\\b9\\a0\\28" \ + "\\12\\3d\\a8\\2e\\d7\\5e\\ef\\65\\2d\\e6\\a5\\67\\84\\ac\\fd\\31" \ + "\\c1\\78\\d8\\72\\51\\a2\\88\\55\\0f\\97\\47\\93\\07\\ea\\8a\\53" \ + "\\27\\4e\\34\\54\\34\\1f\\a0\\6a\\03\\44\\fb\\23\\61\\8e\\87\\8e" \ + "\\3c\\d0\\8f\\ae\\e4\\cf\\ee\\65\\a8\\ba\\96\\68\\08\\1c\\60\\e2" \ + "\\4e\\11\\a3\\74\\b8\\a5\\4e\\ea\\6a\\82\\4c\\c2\\4d\\63\\8e\\9f" \ + "\\7c\\2f\\a8\\c0\\62\\f8\\f7\\d9\\25\\c4\\91\\ab\\4d\\6a\\44\\af" \ + "\\75\\93\\53\\03\\a4\\99\\c8\\cd\\91\\89\\60\\75\\30\\99\\76\\05" \ + "\\5a\\a0\\03\\a7\\a1\\2c\\03\\04\\8f\\d4\\5a\\31\\52\\28\\5a\\e6" \ + "\\a2\\d3\\43\\21\\5b\\dc\\a2\\1d\\55\\a9\\48\\c5\\c4\\aa\\f3\\8b" \ + "\\e6\\3e\\75\\96\\e4\\3e\\64\\af\\e8\\a7\\6a\\b6" + +static void test_sss_certmap_get_search_filter(void **state) +{ + int ret; + struct sss_certmap_ctx *ctx; + char *filter; + char **domains; + const char *dom_list[] = {"test.dom", NULL}; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 100, + "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL", + "LDAP:rule100=<I>{issuer_dn}<S>{subject_dn}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule100=<I>CN=Certificate\\20Authority,O=IPA.DEVEL" + "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule100=<I>CN=Certificate Authority,O=IPA.DEVEL" + "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_null(domains); + + ret = sss_certmap_add_rule(ctx, 99, + "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL", + "LDAP:rule99=<I>{issuer_dn}<S>{subject_dn}", + dom_list); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule99=<I>CN=Certificate\\20Authority,O=IPA.DEVEL" + "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule99=<I>CN=Certificate Authority,O=IPA.DEVEL" + "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_add_rule(ctx, 98, + "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL", + "LDAP:rule98=userCertificate;binary={cert!bin}", + dom_list); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule98=userCertificate;binary=" TEST_CERT_BIN); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule98=userCertificate;binary=" TEST_CERT_BIN); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_add_rule(ctx, 97, + "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL", + "LDAP:rule97=<I>{issuer_dn!nss_x500}<S>{subject_dn}", + dom_list); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule97=<I>O=IPA.DEVEL,CN=Certificate\\20Authority" + "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule97=<I>O=IPA.DEVEL,CN=Certificate Authority" + "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_add_rule(ctx, 96, + "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL", + "LDAP:rule96=<I>{issuer_dn!nss_x500}<S>{subject_dn!nss_x500}", + dom_list); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule96=<I>O=IPA.DEVEL,CN=Certificate\\20Authority" + "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule96=<I>O=IPA.DEVEL,CN=Certificate Authority" + "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_add_rule(ctx, 95, + "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL", + NULL, NULL); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT_BIN ")"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT_BIN ")"); + assert_null(domains); + + ret = sss_certmap_add_rule(ctx, 94, + "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL", + "LDAP:rule94=<I>{issuer_dn!ad_x500}<S>{subject_dn!ad_x500}", + dom_list); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule94=<I>O=IPA.DEVEL,CN=Certificate\\20Authority" + "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert_der), + sizeof(test_cert_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule94=<I>O=IPA.DEVEL,CN=Certificate Authority" + "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel"); + assert_non_null(domains); + assert_string_equal(domains[0], "test.dom"); + assert_null(domains[1]); + + ret = sss_certmap_add_rule(ctx, 89, NULL, + "(rule89={subject_nt_principal})", + NULL); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "(rule89=tu1@ad.devel)"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "(rule89=tu1@ad.devel)"); + assert_null(domains); + + ret = sss_certmap_add_rule(ctx, 88, NULL, + "(rule88={subject_nt_principal.short_name})", + NULL); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "(rule88=tu1)"); + assert_null(domains); + + ret = sss_certmap_add_rule(ctx, 87, NULL, + "LDAP:rule87=<I>{issuer_dn!nss_x500}<S>{subject_dn!nss_x500}", + NULL); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule87=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA" + "<S>DC=devel,DC=ad,CN=Users,CN=t\\20u,E=test.user@email.domain"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule87=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA" + "<S>DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain"); + assert_null(domains); + + ret = sss_certmap_add_rule(ctx, 86, NULL, + "LDAP:rule86=<I>{issuer_dn!ad_x500}<S>{subject_dn!ad_x500}", + NULL); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule86=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA" + "<S>DC=devel,DC=ad,CN=Users,CN=t\\20u,E=test.user@email.domain"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule86=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA" + "<S>DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain"); + assert_null(domains); + + + sss_certmap_free_ctx(ctx); + + /* check defaults when no rules are added yet */ + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT2_BIN")"); + assert_null(domains); + + sss_certmap_free_ctx(ctx); +} + +static void test_sss_certmap_ldapu1_serial_number(void **state) +{ + int ret; + struct sss_certmap_ctx *ctx; + char *filter; + char **domains; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + ret = sss_certmap_add_rule(ctx, 100, + "KRB5:<ISSUER>.*", + "LDAP:rule100={serial_number}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 100, + "KRB5:<ISSUER>.*", + "LDAPU1:rule100={serial_number}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule100=612288c20000000002a6"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule100=612288c20000000002a6"); + assert_null(domains); + + + ret = sss_certmap_add_rule(ctx, 99, + "KRB5:<ISSUER>.*", + "LDAP:rule99={serial_number!HEX_U}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 99, + "KRB5:<ISSUER>.*", + "LDAPU1:rule99={serial_number!HEX_U}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule99=612288C20000000002A6"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule99=612288C20000000002A6"); + assert_null(domains); + + + ret = sss_certmap_add_rule(ctx, 98, + "KRB5:<ISSUER>.*", + "LDAP:rule98={serial_number!HEX_UC}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 98, + "KRB5:<ISSUER>.*", + "LDAPU1:rule98={serial_number!HEX_UC}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule98=61:22:88:C2:00:00:00:00:02:A6"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule98=61:22:88:C2:00:00:00:00:02:A6"); + assert_null(domains); + + + ret = sss_certmap_add_rule(ctx, 97, + "KRB5:<ISSUER>.*", + "LDAP:rule97={serial_number!hex_c}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 97, + "KRB5:<ISSUER>.*", + "LDAPU1:rule97={serial_number!hex_c}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule97=61:22:88:c2:00:00:00:00:02:a6"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule97=61:22:88:c2:00:00:00:00:02:a6"); + assert_null(domains); + + + ret = sss_certmap_add_rule(ctx, 96, + "KRB5:<ISSUER>.*", + "LDAP:rule96={serial_number!hex}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 96, + "KRB5:<ISSUER>.*", + "LDAPU1:rule96={serial_number!hex}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule96=612288c20000000002a6"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule96=612288c20000000002a6"); + assert_null(domains); + + + ret = sss_certmap_add_rule(ctx, 95, + "KRB5:<ISSUER>.*", + "LDAP:rule95={serial_number!dec}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 95, + "KRB5:<ISSUER>.*", + "LDAPU1:rule95={serial_number!dec}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule95=458706592575796350550694"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule95=458706592575796350550694"); + assert_null(domains); + + /* Conversion specifiers are not supported for 'dec' */ + ret = sss_certmap_add_rule(ctx, 94, + "KRB5:<ISSUER>.*", + "LDAP:rule94={serial_number!dec_u}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 94, + "KRB5:<ISSUER>.*", + "LDAPU1:rule94={serial_number!dec_u}", NULL); + assert_int_equal(ret, EINVAL); + + sss_certmap_free_ctx(ctx); +} + +static void test_sss_certmap_ldapu1_subject_key_id(void **state) +{ + int ret; + struct sss_certmap_ctx *ctx; + char *filter; + char **domains; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + /* subject_key_id */ + ret = sss_certmap_add_rule(ctx, 94, + "KRB5:<ISSUER>.*", + "LDAP:rule94={subject_key_id}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 94, + "KRB5:<ISSUER>.*", + "LDAPU1:rule94={subject_key_id}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule94=49acade06530c4cea009035bad4a7b495ec96cb4"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule94=49acade06530c4cea009035bad4a7b495ec96cb4"); + assert_null(domains); + + + /* subject_key_id!HEX */ + ret = sss_certmap_add_rule(ctx, 93, + "KRB5:<ISSUER>.*", + "LDAP:rule93={subject_key_id!HEX_U}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 93, + "KRB5:<ISSUER>.*", + "LDAPU1:rule93={subject_key_id!HEX_U}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule93=49ACADE06530C4CEA009035BAD4A7B495EC96CB4"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule93=49ACADE06530C4CEA009035BAD4A7B495EC96CB4"); + assert_null(domains); + + + /* subject_key_id!HEX_COLON */ + ret = sss_certmap_add_rule(ctx, 92, + "KRB5:<ISSUER>.*", + "LDAP:rule92={subject_key_id!HEX_CU}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 92, + "KRB5:<ISSUER>.*", + "LDAPU1:rule92={subject_key_id!HEX_CU}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule92=49:AC:AD:E0:65:30:C4:CE:A0:09:03:5B:AD:4A:7B:49:5E:C9:6C:B4"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule92=49:AC:AD:E0:65:30:C4:CE:A0:09:03:5B:AD:4A:7B:49:5E:C9:6C:B4"); + assert_null(domains); + + + /* subject_key_id!hex_colon */ + ret = sss_certmap_add_rule(ctx, 91, + "KRB5:<ISSUER>.*", + "LDAP:rule91={subject_key_id!hex_c}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 91, + "KRB5:<ISSUER>.*", + "LDAPU1:rule91={subject_key_id!hex_c}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule91=49:ac:ad:e0:65:30:c4:ce:a0:09:03:5b:ad:4a:7b:49:5e:c9:6c:b4"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule91=49:ac:ad:e0:65:30:c4:ce:a0:09:03:5b:ad:4a:7b:49:5e:c9:6c:b4"); + assert_null(domains); + + + /* subject_key_id!hex */ + ret = sss_certmap_add_rule(ctx, 90, + "KRB5:<ISSUER>.*", + "LDAP:rule90={subject_key_id!hex}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 90, + "KRB5:<ISSUER>.*", + "LDAPU1:rule90={subject_key_id!hex}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule90=49acade06530c4cea009035bad4a7b495ec96cb4"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule90=49acade06530c4cea009035bad4a7b495ec96cb4"); + assert_null(domains); + + + /* UNSUPPORTED subject_key_id!dec */ + ret = sss_certmap_add_rule(ctx, 89, + "KRB5:<ISSUER>.*", + "LDAP:rule89={subject_key_id!dec}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 89, + "KRB5:<ISSUER>.*", + "LDAPU1:rule89={subject_key_id!dec}", NULL); + assert_int_equal(ret, EINVAL); + + sss_certmap_free_ctx(ctx); +} + +static void test_sss_certmap_ldapu1_cert(void **state) +{ + int ret; + struct sss_certmap_ctx *ctx; + char *filter; + char **domains; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + /* cert!sha */ + ret = sss_certmap_add_rule(ctx, 91, + "KRB5:<ISSUER>.*", + "LDAP:rule91={cert!sha}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 91, + "KRB5:<ISSUER>.*", + "LDAPU1:rule91={cert!sha}", NULL); + assert_int_equal(ret, EINVAL); + + /* cert!sha_u */ + ret = sss_certmap_add_rule(ctx, 90, + "KRB5:<ISSUER>.*", + "LDAP:rule90={cert!sha_u}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 99, + "KRB5:<ISSUER>.*", + "LDAPU1:rule90={cert!sha_u}", NULL); + assert_int_equal(ret, EINVAL); + + /* cert!sha555 */ + ret = sss_certmap_add_rule(ctx, 89, + "KRB5:<ISSUER>.*", + "LDAP:rule89={cert!sha555}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 89, + "KRB5:<ISSUER>.*", + "LDAPU1:rule89={cert!sha555}", NULL); + assert_int_equal(ret, EINVAL); + + /* cert!sha512 */ + ret = sss_certmap_add_rule(ctx, 88, + "KRB5:<ISSUER>.*", + "LDAP:rule88={cert!sha512}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 88, + "KRB5:<ISSUER>.*", + "LDAPU1:rule88={cert!sha512}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule88=3ee0583237ae9e6ec7d92dd4a029a9d54147bad48b1b0bad0e4e63380bc9a18ae7b447a03a05b35ad605494a57c359400c0ff3f411c71b96e500f8f2765f6fa3"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule88=3ee0583237ae9e6ec7d92dd4a029a9d54147bad48b1b0bad0e4e63380bc9a18ae7b447a03a05b35ad605494a57c359400c0ff3f411c71b96e500f8f2765f6fa3"); + assert_null(domains); + + /* cert!sha512_u */ + ret = sss_certmap_add_rule(ctx, 68, + "KRB5:<ISSUER>.*", + "LDAP:rule68={cert!sha512_u}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 68, + "KRB5:<ISSUER>.*", + "LDAPU1:rule68={cert!sha512_u}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule68=3EE0583237AE9E6EC7D92DD4A029A9D54147BAD48B1B0BAD0E4E63380BC9A18AE7B447A03A05B35AD605494A57C359400C0FF3F411C71B96E500F8F2765F6FA3"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule68=3EE0583237AE9E6EC7D92DD4A029A9D54147BAD48B1B0BAD0E4E63380BC9A18AE7B447A03A05B35AD605494A57C359400C0FF3F411C71B96E500F8F2765F6FA3"); + assert_null(domains); + + /* cert!SHA512_CU */ + ret = sss_certmap_add_rule(ctx, 67, + "KRB5:<ISSUER>.*", + "LDAP:rule67={cert!SHA512_CU}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 67, + "KRB5:<ISSUER>.*", + "LDAPU1:rule67={cert!SHA512_CU}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule67=3E:E0:58:32:37:AE:9E:6E:C7:D9:2D:D4:A0:29:A9:D5:41:47:BA:D4:8B:1B:0B:AD:0E:4E:63:38:0B:C9:A1:8A:E7:B4:47:A0:3A:05:B3:5A:D6:05:49:4A:57:C3:59:40:0C:0F:F3:F4:11:C7:1B:96:E5:00:F8:F2:76:5F:6F:A3"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule67=3E:E0:58:32:37:AE:9E:6E:C7:D9:2D:D4:A0:29:A9:D5:41:47:BA:D4:8B:1B:0B:AD:0E:4E:63:38:0B:C9:A1:8A:E7:B4:47:A0:3A:05:B3:5A:D6:05:49:4A:57:C3:59:40:0C:0F:F3:F4:11:C7:1B:96:E5:00:F8:F2:76:5F:6F:A3"); + assert_null(domains); + + /* cert!SHA512_CRU */ + ret = sss_certmap_add_rule(ctx, 66, + "KRB5:<ISSUER>.*", + "LDAP:rule66={cert!SHA512_CRU}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 66, + "KRB5:<ISSUER>.*", + "LDAPU1:rule66={cert!SHA512_CRU}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule66=A3:6F:5F:76:F2:F8:00:E5:96:1B:C7:11:F4:F3:0F:0C:40:59:C3:57:4A:49:05:D6:5A:B3:05:3A:A0:47:B4:E7:8A:A1:C9:0B:38:63:4E:0E:AD:0B:1B:8B:D4:BA:47:41:D5:A9:29:A0:D4:2D:D9:C7:6E:9E:AE:37:32:58:E0:3E"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule66=A3:6F:5F:76:F2:F8:00:E5:96:1B:C7:11:F4:F3:0F:0C:40:59:C3:57:4A:49:05:D6:5A:B3:05:3A:A0:47:B4:E7:8A:A1:C9:0B:38:63:4E:0E:AD:0B:1B:8B:D4:BA:47:41:D5:A9:29:A0:D4:2D:D9:C7:6E:9E:AE:37:32:58:E0:3E"); + assert_null(domains); + + sss_certmap_free_ctx(ctx); +} + +static void test_sss_certmap_ldapu1_subject_dn_component(void **state) +{ + int ret; + struct sss_certmap_ctx *ctx; + char *filter; + char **domains; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + /* subject_dn_component */ + ret = sss_certmap_add_rule(ctx, 77, + "KRB5:<ISSUER>.*", + "LDAP:rule77={subject_dn_component}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 77, + "KRB5:<ISSUER>.*", + "LDAPU1:rule77={subject_dn_component}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule77=test.user@email.domain"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule77=test.user@email.domain"); + assert_null(domains); + + /* subject_dn_component.[...] */ + ret = sss_certmap_add_rule(ctx, 76, + "KRB5:<ISSUER>.*", + "LDAP:rule76={subject_dn_component.[1]}--{subject_dn_component.[2]}--{subject_dn_component.[3]}--{subject_dn_component.[4]}--{subject_dn_component.[5]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 76, + "KRB5:<ISSUER>.*", + "LDAPU1:rule76={subject_dn_component.[1]}--{subject_dn_component.[2]}--{subject_dn_component.[3]}--{subject_dn_component.[4]}--{subject_dn_component.[5]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule76=test.user@email.domain--t\\20u--Users--ad--devel"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule76=test.user@email.domain--t u--Users--ad--devel"); + assert_null(domains); + + /* subject_dn_component.[-...] */ + ret = sss_certmap_add_rule(ctx, 75, + "KRB5:<ISSUER>.*", + "LDAP:rule75={subject_dn_component.[-5]}--{subject_dn_component.[-4]}--{subject_dn_component.[-3]}--{subject_dn_component.[-2]}--{subject_dn_component.[-1]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 75, + "KRB5:<ISSUER>.*", + "LDAPU1:rule75={subject_dn_component.[-5]}--{subject_dn_component.[-4]}--{subject_dn_component.[-3]}--{subject_dn_component.[-2]}--{subject_dn_component.[-1]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule75=test.user@email.domain--t\\20u--Users--ad--devel"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule75=test.user@email.domain--t u--Users--ad--devel"); + assert_null(domains); + + /* subject_dn_component.[6] */ + ret = sss_certmap_add_rule(ctx, 74, + "KRB5:<ISSUER>.*", + "LDAP:rule74={subject_dn_component.[6]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 74, + "KRB5:<ISSUER>.*", + "LDAPU1:rule74={subject_dn_component.[6]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, EINVAL); + + /* subject_dn_component.[-6] */ + ret = sss_certmap_add_rule(ctx, 73, + "KRB5:<ISSUER>.*", + "LDAP:rule73={subject_dn_component.[-6]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 73, + "KRB5:<ISSUER>.*", + "LDAPU1:rule73={subject_dn_component.[-6]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, EINVAL); + + /* subject_dn_component.e */ + ret = sss_certmap_add_rule(ctx, 72, + "KRB5:<ISSUER>.*", + "LDAP:rule72={subject_dn_component.e}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 72, + "KRB5:<ISSUER>.*", + "LDAPU1:rule72={subject_dn_component.e}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule72=test.user@email.domain"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule72=test.user@email.domain"); + assert_null(domains); + + /* subject_dn_component.cn */ + ret = sss_certmap_add_rule(ctx, 71, + "KRB5:<ISSUER>.*", + "LDAP:rule71={subject_dn_component.cn}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 71, + "KRB5:<ISSUER>.*", + "LDAPU1:rule71={subject_dn_component.cn}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule71=t\\20u"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule71=t u"); + assert_null(domains); + + /* subject_dn_component.cn[2] */ + ret = sss_certmap_add_rule(ctx, 70, + "KRB5:<ISSUER>.*", + "LDAP:rule70={subject_dn_component.cn[2]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 70, + "KRB5:<ISSUER>.*", + "LDAPU1:rule70={subject_dn_component.cn[2]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule70=t\\20u"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule70=t u"); + assert_null(domains); + + /* subject_dn_component.cn[1] */ + ret = sss_certmap_add_rule(ctx, 69, + "KRB5:<ISSUER>.*", + "LDAP:rule69={subject_dn_component.cn[1]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 69, + "KRB5:<ISSUER>.*", + "LDAPU1:rule60={subject_dn_component.cn[1]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, EINVAL); + + sss_certmap_free_ctx(ctx); +} + +static void test_sss_certmap_ldapu1_issuer_dn_component(void **state) +{ + int ret; + struct sss_certmap_ctx *ctx; + char *filter; + char **domains; + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + /* issuer_dn_component */ + ret = sss_certmap_add_rule(ctx, 87, + "KRB5:<ISSUER>.*", + "LDAP:rule87={issuer_dn_component}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 87, + "KRB5:<ISSUER>.*", + "LDAPU1:rule87={issuer_dn_component}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule87=ad-AD-SERVER-CA"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule87=ad-AD-SERVER-CA"); + assert_null(domains); + + /* issuer_dn_component.[0] */ + ret = sss_certmap_add_rule(ctx, 86, + "KRB5:<ISSUER>.*", + "LDAP:rule86={issuer_dn_component.[0]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 86, + "KRB5:<ISSUER>.*", + "LDAPU1:rule86={issuer_dn_component.[0]}", NULL); + assert_int_equal(ret, EINVAL); + + /* issuer_dn_component.[1] */ + ret = sss_certmap_add_rule(ctx, 85, + "KRB5:<ISSUER>.*", + "LDAP:rule85={issuer_dn_component.[1]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 85, + "KRB5:<ISSUER>.*", + "LDAPU1:rule85={issuer_dn_component.[1]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule85=ad-AD-SERVER-CA"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule85=ad-AD-SERVER-CA"); + assert_null(domains); + + /* issuer_dn_component.[-1] */ + ret = sss_certmap_add_rule(ctx, 84, + "KRB5:<ISSUER>.*", + "LDAP:rule84={issuer_dn_component.[-1]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 84, + "KRB5:<ISSUER>.*", + "LDAPU1:rule84={issuer_dn_component.[-1]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule84=devel"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule84=devel"); + assert_null(domains); + + /* issuer_dn_component.[2] */ + ret = sss_certmap_add_rule(ctx, 83, + "KRB5:<ISSUER>.*", + "LDAP:rule83={issuer_dn_component.[2]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 83, + "KRB5:<ISSUER>.*", + "LDAPU1:rule83={issuer_dn_component.[2]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule83=ad"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule83=ad"); + assert_null(domains); + + /* issuer_dn_component.[-2] */ + ret = sss_certmap_add_rule(ctx, 82, + "KRB5:<ISSUER>.*", + "LDAP:rule82={issuer_dn_component.[-2]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 82, + "KRB5:<ISSUER>.*", + "LDAPU1:rule82={issuer_dn_component.[-2]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule82=ad"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule82=ad"); + assert_null(domains); + + /* issuer_dn_component.[3] */ + ret = sss_certmap_add_rule(ctx, 81, + "KRB5:<ISSUER>.*", + "LDAP:rule81={issuer_dn_component.[3]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 81, + "KRB5:<ISSUER>.*", + "LDAPU1:rule81={issuer_dn_component.[3]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule81=devel"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule81=devel"); + assert_null(domains); + + /* issuer_dn_component.[-3] */ + ret = sss_certmap_add_rule(ctx, 80, + "KRB5:<ISSUER>.*", + "LDAP:rule80={issuer_dn_component.[-3]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 80, + "KRB5:<ISSUER>.*", + "LDAPU1:rule80={issuer_dn_component.[-3]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule80=ad-AD-SERVER-CA"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule80=ad-AD-SERVER-CA"); + assert_null(domains); + + /* issuer_dn_component.[4] */ + ret = sss_certmap_add_rule(ctx, 79, + "KRB5:<ISSUER>.*", + "LDAP:rule79={issuer_dn_component.[4]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 79, + "KRB5:<ISSUER>.*", + "LDAPU1:rule79={issuer_dn_component.[4]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, EINVAL); + + /* issuer_dn_component.[-4] */ + ret = sss_certmap_add_rule(ctx, 78, + "KRB5:<ISSUER>.*", + "LDAP:rule78={issuer_dn_component.[-4]}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 78, + "KRB5:<ISSUER>.*", + "LDAPU1:rule78={issuer_dn_component.[-4]}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der), + sizeof(test_cert2_der), + &filter, &domains); + assert_int_equal(ret, EINVAL); + + sss_certmap_free_ctx(ctx); +} + +static void test_sss_certmap_ldapu1_sid(void **state) +{ + int ret; + struct sss_certmap_ctx *ctx; + char *filter; + char **domains; + + uint8_t *der; + size_t der_size; + + der = sss_base64_decode(NULL, TEST_CERT_WITH_SID_EXT, &der_size); + assert_non_null(der); + + ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + assert_null(ctx->prio_list); + + /* full sid */ + ret = sss_certmap_add_rule(ctx, 100, + "KRB5:<ISSUER>.*", + "LDAP:rule100={sid}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 100, + "KRB5:<ISSUER>.*", + "LDAPU1:rule100={sid}", NULL); + assert_int_equal(ret, 0); + + ret = sss_certmap_get_search_filter(ctx, der, der_size, + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule100=S-1-5-21-294026126-3378245018-1203103949-1001"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, der, der_size, + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule100=S-1-5-21-294026126-3378245018-1203103949-1001"); + assert_null(domains); + + /* invalid component */ + ret = sss_certmap_add_rule(ctx, 99, + "KRB5:<ISSUER>.*", + "LDAP:rule99={sid.abc}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 99, + "KRB5:<ISSUER>.*", + "LDAPU1:rule99={sid.abc}", NULL); + assert_int_equal(ret, EINVAL); + + /* rid component */ + ret = sss_certmap_add_rule(ctx, 98, + "KRB5:<ISSUER>.*", + "LDAP:rule98={sid.rid}", NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_certmap_add_rule(ctx, 98, + "KRB5:<ISSUER>.*", + "LDAPU1:rule98={sid.rid}", NULL); + assert_int_equal(ret, 0); + ret = sss_certmap_get_search_filter(ctx, der, der_size, + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule98=1001"); + assert_null(domains); + + ret = sss_certmap_expand_mapping_rule(ctx, der, der_size, + &filter, &domains); + assert_int_equal(ret, 0); + assert_non_null(filter); + assert_string_equal(filter, "rule98=1001"); + assert_null(domains); + + sss_certmap_free_ctx(ctx); +} + + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sss_certmap_init), + cmocka_unit_test(test_sss_certmap_add_rule), + cmocka_unit_test(test_sss_certmap_add_matching_rule), + cmocka_unit_test(test_check_ad_attr_name), + cmocka_unit_test(test_sss_cert_get_content), + cmocka_unit_test(test_sss_cert_get_content_2), +#ifdef HAVE_TEST_CA + cmocka_unit_test(test_sss_cert_get_content_test_cert_0003), + cmocka_unit_test(test_sss_cert_get_content_test_cert_0004), + cmocka_unit_test(test_sss_cert_get_content_test_cert_0001), +#endif + cmocka_unit_test(test_sss_cert_get_content_test_cert_with_sid_ext), + cmocka_unit_test(test_sss_certmap_match_cert), + cmocka_unit_test(test_sss_certmap_add_mapping_rule), + cmocka_unit_test(test_sss_certmap_get_search_filter), + cmocka_unit_test(test_sss_certmap_ldapu1_serial_number), + cmocka_unit_test(test_sss_certmap_ldapu1_subject_key_id), + cmocka_unit_test(test_sss_certmap_ldapu1_cert), + cmocka_unit_test(test_sss_certmap_ldapu1_subject_dn_component), + cmocka_unit_test(test_sss_certmap_ldapu1_issuer_dn_component), + cmocka_unit_test(test_sss_certmap_ldapu1_sid), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + CRYPTO_cleanup_all_ex_data(); /* to make Valgrind happy */ + + return rv; +} diff --git a/src/tests/cmocka/test_child_common.c b/src/tests/cmocka/test_child_common.c new file mode 100644 index 0000000..18d5b0b --- /dev/null +++ b/src/tests/cmocka/test_child_common.c @@ -0,0 +1,623 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: Child handlers + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> + +#include "util/child_common.h" +#include "tests/cmocka/common_mock.h" + +#define TEST_BIN "dummy-child" +#define ECHO_STR "Hello child" +#define ECHO_LARGE_STR "Lorem ipsum dolor sit amet consectetur adipiscing elit, urna consequat felis vehicula class ultricies mollis dictumst, aenean non a in donec nulla. Phasellus ante pellentesque erat cum risus consequat imperdiet aliquam, integer placerat et turpis mi eros nec lobortis taciti, vehicula nisl litora tellus ligula porttitor metus. Vivamus integer non suscipit taciti mus etiam at primis tempor sagittis sit, euismod libero facilisi aptent elementum felis blandit cursus gravida sociis erat ante, eleifend lectus nullam dapibus netus feugiat curae curabitur est ad. Massa curae fringilla porttitor quam sollicitudin iaculis aptent leo ligula euismod dictumst, orci penatibus mauris eros etiam praesent erat volutpat posuere hac. Metus fringilla nec ullamcorper odio aliquam lacinia conubia mauris tempor, etiam ultricies proin quisque lectus sociis id tristique, integer phasellus taciti pretium adipiscing tortor sagittis ligula. Mollis pretium lorem primis senectus habitasse lectus scelerisque donec, ultricies tortor suspendisse adipiscing fusce morbi volutpat pellentesque, consectetur mi risus molestie curae malesuada cum. Dignissim lacus convallis massa mauris enim ad mattis magnis senectus montes, mollis taciti phasellus accumsan bibendum semper blandit suspendisse faucibus nibh est, metus lobortis morbi cras magna vivamus per risus fermentum. Dapibus imperdiet praesent magnis ridiculus congue gravida curabitur dictum sagittis, enim et magna sit inceptos sodales parturient pharetra mollis, aenean vel nostra tellus commodo pretium sapien sociosqu." + + +static int destructor_called; + +struct child_test_ctx { + int pipefd_to_child[2]; + int pipefd_from_child[2]; + + struct sss_test_ctx *test_ctx; + + int save_debug_timestamps; +}; + +static int child_test_setup(void **state) +{ + struct child_test_ctx *child_tctx; + errno_t ret; + + assert_true(leak_check_setup()); + + child_tctx = talloc(global_talloc_context, struct child_test_ctx); + assert_non_null(child_tctx); + + child_tctx->test_ctx = create_ev_test_ctx(child_tctx); + assert_non_null(child_tctx->test_ctx); + + ret = pipe(child_tctx->pipefd_from_child); + assert_int_not_equal(ret, -1); + DEBUG(SSSDBG_TRACE_LIBS, "from_child: %d:%d\n", + child_tctx->pipefd_from_child[0], + child_tctx->pipefd_from_child[1]); + + ret = pipe(child_tctx->pipefd_to_child); + assert_int_not_equal(ret, -1); + DEBUG(SSSDBG_TRACE_LIBS, "to_child: %d:%d\n", + child_tctx->pipefd_to_child[0], + child_tctx->pipefd_to_child[1]); + + *state = child_tctx; + return 0; +} + +static int child_test_teardown(void **state) +{ + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + + talloc_free(child_tctx); + + assert_true(leak_check_teardown()); + return 0; +} + +/* Just make sure the exec works. The child does nothing but exits */ +void test_exec_child(void **state) +{ + errno_t ret; + pid_t child_pid; + int status; + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + + child_pid = fork(); + assert_int_not_equal(child_pid, -1); + if (child_pid == 0) { + exec_child(child_tctx, + child_tctx->pipefd_to_child, + child_tctx->pipefd_from_child, + CHILD_DIR"/"TEST_BIN, NULL); + } else { + do { + errno = 0; + ret = waitpid(child_pid, &status, 0); + } while (ret == -1 && errno == EINTR); + + if (ret > 0) { + ret = EIO; + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + assert_int_equal(ret, 0); + } + } else { + DEBUG(SSSDBG_FUNC_DATA, + "Failed to wait for children %d\n", child_pid); + ret = EIO; + } + } +} + +static int only_extra_args_setup(void **state) +{ + struct child_test_ctx *child_tctx; + errno_t ret; + + ret = child_test_setup((void **) &child_tctx); + if (ret != 0) { + return ret; + } + + child_tctx->save_debug_timestamps = debug_timestamps; + *state = child_tctx; + + return 0; +} + +static int only_extra_args_teardown(void **state) +{ + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + errno_t ret; + + debug_timestamps = child_tctx->save_debug_timestamps; + ret = child_test_teardown((void **) &child_tctx); + if (ret != 0) { + return ret; + } + + return 0; +} + +static void extra_args_test(struct child_test_ctx *child_tctx, + bool extra_args_only) +{ + pid_t child_pid; + errno_t ret; + int status; + + const char *extra_args[] = { "--guitar=george", + "--drums=ringo", + NULL }; + + child_pid = fork(); + assert_int_not_equal(child_pid, -1); + if (child_pid == 0) { + debug_timestamps = SSSDBG_TIMESTAMP_ENABLED; + + exec_child_ex(child_tctx, + child_tctx->pipefd_to_child, + child_tctx->pipefd_from_child, + CHILD_DIR"/"TEST_BIN, NULL, extra_args, + extra_args_only, + STDIN_FILENO, STDOUT_FILENO); + } else { + do { + errno = 0; + ret = waitpid(child_pid, &status, 0); + } while (ret == -1 && errno == EINTR); + + if (ret > 0) { + ret = EIO; + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + assert_int_equal(ret, 0); + } + } else { + DEBUG(SSSDBG_FUNC_DATA, + "Failed to wait for children %d\n", child_pid); + ret = EIO; + } + } +} + +/* Make sure extra arguments are passed correctly */ +void test_exec_child_extra_args(void **state) +{ + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + setenv("TEST_CHILD_ACTION", "check_extra_args", 1); + extra_args_test(child_tctx, false); +} + +/* Make sure extra arguments are passed correctly */ +void test_exec_child_only_extra_args(void **state) +{ + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + setenv("TEST_CHILD_ACTION", "check_only_extra_args", 1); + extra_args_test(child_tctx, true); +} + +void test_exec_child_only_extra_args_neg(void **state) +{ + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + setenv("TEST_CHILD_ACTION", "check_only_extra_args_neg", 1); + extra_args_test(child_tctx, false); +} + +struct tevent_req *echo_child_write_send(TALLOC_CTX *mem_ctx, + struct child_test_ctx *child_tctx, + struct child_io_fds *io_fds, + const char *input, + bool safe); +static void echo_child_write_done(struct tevent_req *subreq); +static void echo_child_read_done(struct tevent_req *subreq); + +int __real_child_io_destructor(void *ptr); + +int __wrap_child_io_destructor(void *ptr) +{ + destructor_called = 1; + return __real_child_io_destructor(ptr); +} + +/* Test that writing to the pipes works as expected */ +void test_exec_child_io_destruct(void **state) +{ + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + struct child_io_fds *io_fds; + + io_fds = talloc(child_tctx, struct child_io_fds); + io_fds->read_from_child_fd = -1; + io_fds->write_to_child_fd = -1; + assert_non_null(io_fds); + talloc_set_destructor((void *) io_fds, child_io_destructor); + + io_fds->read_from_child_fd = child_tctx->pipefd_from_child[0]; + io_fds->write_to_child_fd = child_tctx->pipefd_to_child[1]; + + destructor_called = 0; + talloc_free(io_fds); + assert_int_equal(destructor_called, 1); + + errno = 0; + close(child_tctx->pipefd_from_child[0]); + assert_int_equal(errno, EBADF); + + errno = 0; + close(child_tctx->pipefd_from_child[1]); + assert_int_equal(errno, 0); + + errno = 0; + close(child_tctx->pipefd_to_child[0]); + assert_int_equal(errno, 0); + + errno = 0; + close(child_tctx->pipefd_to_child[1]); + assert_int_equal(errno, EBADF); +} + +void test_child_cb(int child_status, + struct tevent_signal *sige, + void *pvt); + +/* Test that writing to the pipes works as expected */ +void test_exec_child_handler(void **state) +{ + errno_t ret; + pid_t child_pid; + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + struct sss_child_ctx_old *child_old_ctx; + + ret = unsetenv("TEST_CHILD_ACTION"); + assert_int_equal(ret, 0); + + child_pid = fork(); + assert_int_not_equal(child_pid, -1); + if (child_pid == 0) { + exec_child(child_tctx, + child_tctx->pipefd_to_child, + child_tctx->pipefd_from_child, + CHILD_DIR"/"TEST_BIN, NULL); + } + + ret = child_handler_setup(child_tctx->test_ctx->ev, child_pid, + test_child_cb, child_tctx, &child_old_ctx); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(child_tctx->test_ctx); + assert_int_equal(ret, EOK); + assert_int_equal(child_tctx->test_ctx->error, 0); +} + +void test_child_cb(int child_status, + struct tevent_signal *sige, + void *pvt) +{ + struct child_test_ctx *child_ctx = talloc_get_type(pvt, struct child_test_ctx); + + child_ctx->test_ctx->error = EIO; + if (WIFEXITED(child_status) && WEXITSTATUS(child_status) == 0) { + child_ctx->test_ctx->error = 0; + } + + child_ctx->test_ctx->done = true; +} + +void test_exec_child_echo(void **state, + const char *msg, + bool safe) +{ + + errno_t ret; + pid_t child_pid; + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + struct tevent_req *req; + struct child_io_fds *io_fds; + + setenv("TEST_CHILD_ACTION", "echo", 1); + + io_fds = talloc(child_tctx, struct child_io_fds); + assert_non_null(io_fds); + io_fds->read_from_child_fd = -1; + io_fds->write_to_child_fd = -1; + talloc_set_destructor((void *) io_fds, child_io_destructor); + + child_pid = fork(); + assert_int_not_equal(child_pid, -1); + if (child_pid == 0) { + exec_child_ex(child_tctx, + child_tctx->pipefd_to_child, + child_tctx->pipefd_from_child, + CHILD_DIR"/"TEST_BIN, NULL, NULL, false, + STDIN_FILENO, 3); + } + + DEBUG(SSSDBG_FUNC_DATA, "Forked into %d\n", child_pid); + + io_fds->read_from_child_fd = child_tctx->pipefd_from_child[0]; + close(child_tctx->pipefd_from_child[1]); + io_fds->write_to_child_fd = child_tctx->pipefd_to_child[1]; + close(child_tctx->pipefd_to_child[0]); + + sss_fd_nonblocking(io_fds->write_to_child_fd); + sss_fd_nonblocking(io_fds->read_from_child_fd); + + ret = child_handler_setup(child_tctx->test_ctx->ev, child_pid, + NULL, NULL, NULL); + assert_int_equal(ret, EOK); + + req = echo_child_write_send(child_tctx, child_tctx, io_fds, msg, safe); + assert_non_null(req); + + ret = test_ev_loop(child_tctx->test_ctx); + talloc_free(io_fds); + assert_int_equal(ret, EOK); +} + +/* Test that writing a small message to the pipes works as expected */ +void test_exec_child_echo_small(void **state) +{ + test_exec_child_echo(state, ECHO_STR, false); +} + +void test_exec_child_echo_small_safe(void **state) +{ + test_exec_child_echo(state, ECHO_STR, true); +} + +/* Test that writing a large message to the pipes works as expected, + * test will still fail if message exceeds IN_BUF_SIZE */ +void test_exec_child_echo_large(void **state) +{ + test_exec_child_echo(state, ECHO_LARGE_STR, false); +} + +void test_exec_child_echo_large_safe(void **state) +{ + test_exec_child_echo(state, ECHO_LARGE_STR, true); +} + +struct test_exec_echo_state { + struct child_io_fds *io_fds; + struct io_buffer buf; + struct child_test_ctx *child_test_ctx; + bool safe; +}; + +struct tevent_req *echo_child_write_send(TALLOC_CTX *mem_ctx, + struct child_test_ctx *child_tctx, + struct child_io_fds *io_fds, + const char *input, + bool safe) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct test_exec_echo_state *echo_state; + + req = tevent_req_create(mem_ctx, &echo_state, struct test_exec_echo_state); + assert_non_null(req); + + echo_state->child_test_ctx = child_tctx; + + echo_state->buf.data = (unsigned char *) talloc_strdup(echo_state, input); + assert_non_null(echo_state->buf.data); + echo_state->buf.size = strlen(input) + 1; + echo_state->io_fds = io_fds; + echo_state->safe = safe; + + DEBUG(SSSDBG_TRACE_INTERNAL, "Writing..\n"); + if (echo_state->safe) { + subreq = write_pipe_safe_send(child_tctx, child_tctx->test_ctx->ev, + echo_state->buf.data, echo_state->buf.size, + echo_state->io_fds->write_to_child_fd); + } else { + subreq = write_pipe_send(child_tctx, child_tctx->test_ctx->ev, + echo_state->buf.data, echo_state->buf.size, + echo_state->io_fds->write_to_child_fd); + } + assert_non_null(subreq); + tevent_req_set_callback(subreq, echo_child_write_done, req); + + return req; +} + +static void echo_child_write_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct test_exec_echo_state *echo_state; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + echo_state = tevent_req_data(req, struct test_exec_echo_state); + + if (echo_state->safe) { + ret = write_pipe_safe_recv(subreq); + } else { + ret = write_pipe_recv(subreq); + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "Writing OK\n"); + talloc_zfree(subreq); + assert_int_equal(ret, EOK); + + close(echo_state->io_fds->write_to_child_fd); + echo_state->io_fds->write_to_child_fd = -1; + + DEBUG(SSSDBG_TRACE_INTERNAL, "Reading..\n"); + if (echo_state->safe) { + subreq = read_pipe_safe_send(echo_state, + echo_state->child_test_ctx->test_ctx->ev, + echo_state->io_fds->read_from_child_fd); + } else { + subreq = read_pipe_send(echo_state, + echo_state->child_test_ctx->test_ctx->ev, + echo_state->io_fds->read_from_child_fd); + } + + assert_non_null(subreq); + tevent_req_set_callback(subreq, echo_child_read_done, req); +} + +static void echo_child_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct test_exec_echo_state *echo_state; + errno_t ret; + ssize_t len; + uint8_t *buf; + + req = tevent_req_callback_data(subreq, struct tevent_req); + echo_state = tevent_req_data(req, struct test_exec_echo_state); + + if (echo_state->safe) { + ret = read_pipe_safe_recv(subreq, echo_state, &buf, &len); + } else { + ret = read_pipe_recv(subreq, echo_state, &buf, &len); + } + + talloc_zfree(subreq); + DEBUG(SSSDBG_TRACE_INTERNAL, "Reading OK\n"); + assert_int_equal(ret, EOK); + + close(echo_state->io_fds->read_from_child_fd); + echo_state->io_fds->read_from_child_fd = -1; + + assert_string_equal((char *)buf, (char *)echo_state->buf.data); + echo_state->child_test_ctx->test_ctx->done = true; +} + +void sss_child_cb(int pid, int wait_status, void *pvt); + +/* Just make sure the exec works. The child does nothing but exits */ +void test_sss_child(void **state) +{ + errno_t ret; + pid_t child_pid; + struct child_test_ctx *child_tctx = talloc_get_type(*state, + struct child_test_ctx); + struct sss_sigchild_ctx *sc_ctx; + struct sss_child_ctx *sss_child; + + ret = unsetenv("TEST_CHILD_ACTION"); + assert_int_equal(ret, 0); + + ret = sss_sigchld_init(child_tctx, child_tctx->test_ctx->ev, &sc_ctx); + assert_int_equal(ret, EOK); + + child_pid = fork(); + assert_int_not_equal(child_pid, -1); + if (child_pid == 0) { + exec_child(child_tctx, + child_tctx->pipefd_to_child, + child_tctx->pipefd_from_child, + CHILD_DIR"/"TEST_BIN, NULL); + } + + ret = sss_child_register(child_tctx, sc_ctx, + child_pid, + sss_child_cb, + child_tctx, &sss_child); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(child_tctx->test_ctx); + assert_int_equal(ret, EOK); + assert_int_equal(child_tctx->test_ctx->error, 0); +} + +void sss_child_cb(int pid, int wait_status, void *pvt) +{ + struct child_test_ctx *child_ctx = talloc_get_type(pvt, struct child_test_ctx); + + child_ctx->test_ctx->error = EIO; + if (WIFEXITED(wait_status) && WEXITSTATUS(wait_status) == 0) { + child_ctx->test_ctx->error = 0; + } + + child_ctx->test_ctx->done = true; +} + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_exec_child, + child_test_setup, + child_test_teardown), + cmocka_unit_test_setup_teardown(test_exec_child_extra_args, + child_test_setup, + child_test_teardown), + cmocka_unit_test_setup_teardown(test_exec_child_io_destruct, + child_test_setup, + child_test_teardown), + cmocka_unit_test_setup_teardown(test_exec_child_handler, + child_test_setup, + child_test_teardown), + cmocka_unit_test_setup_teardown(test_exec_child_echo_small, + child_test_setup, + child_test_teardown), + cmocka_unit_test_setup_teardown(test_exec_child_echo_large, + child_test_setup, + child_test_teardown), + cmocka_unit_test_setup_teardown(test_exec_child_echo_small_safe, + child_test_setup, + child_test_teardown), + cmocka_unit_test_setup_teardown(test_exec_child_echo_large_safe, + child_test_setup, + child_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_child, + child_test_setup, + child_test_teardown), + cmocka_unit_test_setup_teardown(test_exec_child_only_extra_args, + only_extra_args_setup, + only_extra_args_teardown), + cmocka_unit_test_setup_teardown(test_exec_child_only_extra_args_neg, + only_extra_args_setup, + only_extra_args_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + return rv; +} diff --git a/src/tests/cmocka/test_config_check.c b/src/tests/cmocka/test_config_check.c new file mode 100644 index 0000000..2de05f8 --- /dev/null +++ b/src/tests/cmocka/test_config_check.c @@ -0,0 +1,344 @@ +/* + Authors: + Michal Zidek <mzidek@redhat.com> + + Copyright (C) 2017 Red Hat + + Config file validators test + + 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 <popt.h> +#include <talloc.h> +#include <ini_configobj.h> + +#include "util/sss_ini.h" +#include "tests/cmocka/common_mock.h" + +#ifdef HAVE_LIBINI_CONFIG_V1_3 + +#define RULES_PATH ABS_SRC_DIR"/src/config/cfg_rules.ini" + +struct sss_ini { + char **error_list; + struct ref_array *ra_success_list; + struct ref_array *ra_error_list; + struct ini_cfgobj *sssd_config; + struct value_obj *obj; + const struct stat *cstat; + struct ini_cfgfile *file; + bool main_config_exists; +}; + +void config_check_test_common(const char *cfg_string, + size_t num_errors_expected, + const char **errors_expected) +{ + struct sss_ini *init_data; + size_t num_errors; + char **strs; + int ret; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + + init_data = sss_ini_new(tmp_ctx); + + ret = sss_ini_open(init_data, NULL, cfg_string); + assert_int_equal(ret, EOK); + + ret = ini_config_create(&(init_data->sssd_config)); + assert_int_equal(ret, EOK); + + ret = ini_config_parse(init_data->file, + INI_STOP_ON_ANY, + INI_MV1S_OVERWRITE, + INI_PARSE_NOWRAP, + init_data->sssd_config); + assert_int_equal(ret, EOK); + + ret = sss_ini_call_validators_strs(tmp_ctx, init_data, + RULES_PATH, + &strs, &num_errors); + assert_int_equal(ret, EOK); + + /* Output from validators */ + for (int i = 0; i < num_errors; i++) { + /* Keep this printf loop for faster debugging */ + printf("%s\n", strs[i]); + } + assert_int_equal(num_errors, num_errors_expected); + + for (int i = 0; i < num_errors && i <= num_errors_expected; i++) { + assert_string_equal(strs[i], errors_expected[i]); + } + + /* Check if the number of errors is the same */ + assert_int_equal(num_errors_expected, num_errors); + + talloc_free(tmp_ctx); +} + +void config_check_test_bad_section_name(void **state) +{ + char cfg_str[] = "[sssssssssssssd]"; + const char *expected_errors[] = { + "[rule/allowed_sections]: Section [sssssssssssssd] is not allowed. " + "Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_chars_in_section_name(void **state) +{ + char cfg_str[] = "[domain/LD@P]\n" + "id_provider = ldap\n"; + const char *expected_errors[] = { + "[rule/allowed_sections]: Section [domain/LD@P] is not allowed. " + "Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_too_many_subdomains(void **state) +{ + char cfg_str[] = "[domain/ad.test/b.test/c.test]"; + const char *expected_errors[] = { + "[rule/allowed_sections]: Section [domain/ad.test/b.test/c.test] is not allowed. " + "Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_sssd_option_name(void **state) +{ + char cfg_str[] = "[sssd]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_sssd_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'sssd'. Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_pam_option_name(void **state) +{ + char cfg_str[] = "[pam]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_pam_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'pam'. Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_nss_option_name(void **state) +{ + char cfg_str[] = "[nss]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_nss_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'nss'. Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_pac_option_name(void **state) +{ + char cfg_str[] = "[pac]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_pac_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'pac'. Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_ifp_option_name(void **state) +{ + char cfg_str[] = "[ifp]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_ifp_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'ifp'. Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_domain_option_name(void **state) +{ + char cfg_str[] = "[domain/A.test]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'domain/A.test'. Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_appdomain_option_name(void **state) +{ + char cfg_str[] = "[application/myapp]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_domain_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'application/myapp'. Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_subdom_option_name(void **state) +{ + char cfg_str[] = "[domain/A.test/B.A.test]\n" + "debug_leTYPOvel = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_subdomain_options]: Attribute 'debug_leTYPOvel' is not " + "allowed in section 'domain/A.test/B.A.test'. Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_bad_certmap_option_name(void **state) +{ + char cfg_str[] = "[certmap/files/testuser]\n" + "debug_level = 10\n"; + const char *expected_errors[] = { + "[rule/allowed_certmap_options]: Attribute 'debug_level' is not " + "allowed in section 'certmap/files/testuser'. Check for typos.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_good_sections(void **state) +{ + char cfg_str[] = "[sssd]\n" + "[pam]\n" + "[nss]\n" + "[domain/testdom.test]\n" + "id_provider = proxy\n" + "[domain/testdom.test/testsubdom.testdom.test]\n" + "[application/myapp]\n" + "[ssh]\n" + "[ifp]\n" + "[pac]\n" + "[certmap/files/testuser]\n"; + const char *expected_errors[] = { NULL }; + + config_check_test_common(cfg_str, 0, expected_errors); +} + +void config_check_test_missing_id_provider(void **state) +{ + char cfg_str[] = "[domain/A.test]\n"; + const char *expected_errors[] = { + "[rule/sssd_checks]: Attribute 'id_provider' is missing in " + "section 'domain/A.test'.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_inherit_from_in_normal_dom(void **state) +{ + char cfg_str[] = "[domain/A.test]\n" + "id_provider = proxy\n" + "inherit_from = domain\n"; + const char *expected_errors[] = { + "[rule/sssd_checks]: Attribute 'inherit_from' is not allowed in " + "section 'domain/A.test'.", + }; + + config_check_test_common(cfg_str, 1, expected_errors); +} + +void config_check_test_inherit_from_in_app_dom(void **state) +{ + char cfg_str[] = "[application/A.test]\n" + "inherit_from = domain\n"; + const char *expected_errors[] = { NULL }; + + config_check_test_common(cfg_str, 0, expected_errors); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(config_check_test_bad_section_name), + cmocka_unit_test(config_check_test_bad_chars_in_section_name), + cmocka_unit_test(config_check_test_too_many_subdomains), + cmocka_unit_test(config_check_test_bad_sssd_option_name), + cmocka_unit_test(config_check_test_bad_pam_option_name), + cmocka_unit_test(config_check_test_bad_nss_option_name), + cmocka_unit_test(config_check_test_bad_pac_option_name), + cmocka_unit_test(config_check_test_bad_ifp_option_name), + cmocka_unit_test(config_check_test_bad_appdomain_option_name), + cmocka_unit_test(config_check_test_bad_subdom_option_name), + cmocka_unit_test(config_check_test_bad_certmap_option_name), + cmocka_unit_test(config_check_test_good_sections), + cmocka_unit_test(config_check_test_missing_id_provider), + cmocka_unit_test(config_check_test_inherit_from_in_normal_dom), + cmocka_unit_test(config_check_test_inherit_from_in_app_dom), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + tests_set_cwd(); + return cmocka_run_group_tests(tests, NULL, NULL); +} + +#else /* !HAVE_LIBINI_CONFIG_V1_3 */ + +int main(int argc, const char *argv[]) +{ + fprintf(stderr, "%s requires newer version of libini\n", argv[0]); + return 0; +} + +#endif /* HAVE_LIBINI_CONFIG_V1_3 */ diff --git a/src/tests/cmocka/test_copy_ccache.c b/src/tests/cmocka/test_copy_ccache.c new file mode 100644 index 0000000..7c76c00 --- /dev/null +++ b/src/tests/cmocka/test_copy_ccache.c @@ -0,0 +1,240 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: Tests ccache utilities + + 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 <stdio.h> +#include <popt.h> + +#include "util/sss_krb5.h" +#include "providers/krb5/krb5_common.h" +#include "providers/krb5/krb5_ccache.h" +#include "tests/cmocka/common_mock.h" + +#define CCACHE_TEST_CLIENT_PRINC "test/client@TEST.CCACHE" +#define CCACHE_TEST_SERVER_PRINC "test/server@TEST.CCACHE" +#define CCACHE_PATH TEST_DIR "/ccache_test.ccache" + +struct ccache_test_ctx { + krb5_context kctx; + const char *ccache_file_name; + krb5_principal client_principal; + krb5_principal server_principal; +}; + +static int setup_ccache(void **state) +{ + struct ccache_test_ctx *test_ctx; + krb5_error_code kerr; + krb5_ccache ccache; + krb5_creds test_creds; + static krb5_address addr; + int add=0x12345; + krb5_authdata *a; + + static krb5_address *addrs[] = { + &addr, + NULL, + }; + + assert_true(leak_check_setup()); + + + test_ctx = talloc_zero(global_talloc_context, struct ccache_test_ctx); + assert_non_null(test_ctx); + + kerr = krb5_init_context(&test_ctx->kctx); + assert_int_equal(kerr, 0); + + addr.magic = KV5M_ADDRESS; + addr.addrtype = ADDRTYPE_INET; + addr.length = 4; + addr.contents = (krb5_octet *) &add; + + memset(&test_creds, 0, sizeof(test_creds)); + test_creds.magic = KV5M_CREDS; + kerr = krb5_parse_name(test_ctx->kctx, CCACHE_TEST_CLIENT_PRINC, + &test_ctx->client_principal); + assert_int_equal(kerr, 0); + test_creds.client = test_ctx->client_principal; + kerr = krb5_parse_name(test_ctx->kctx, CCACHE_TEST_SERVER_PRINC, + &test_ctx->server_principal); + assert_int_equal(kerr, 0); + test_creds.server = test_ctx->server_principal; + + test_creds.keyblock.magic = KV5M_KEYBLOCK; + test_creds.keyblock.contents = 0; + test_creds.keyblock.enctype = 1; + test_creds.keyblock.length = 1; + test_creds.keyblock.contents = (unsigned char *) discard_const("1"); + test_creds.times.authtime = 1111; + test_creds.times.starttime = 2222; + test_creds.times.endtime = 3333; + test_creds.times.renew_till = 4444; + test_creds.is_skey = 0; + test_creds.ticket_flags = 5555; + test_creds.addresses = addrs; + + test_creds.ticket.magic = KV5M_DATA; + test_creds.ticket.length = sizeof("Ticket"); + test_creds.ticket.data = discard_const("Ticket"); + + test_creds.authdata = malloc (2 * sizeof(krb5_authdata *)); + assert_non_null(test_creds.authdata); + + a = (krb5_authdata *) malloc(sizeof(krb5_authdata)); + assert_non_null(a); + + a->magic = KV5M_AUTHDATA; + a->ad_type = KRB5_AUTHDATA_IF_RELEVANT; + a->contents = (krb5_octet * ) malloc(1); + assert_non_null(a->contents); + a->contents[0]=5; + a->length = 1; + test_creds.authdata[0] = a; + test_creds.authdata[1] = NULL; + + + test_ctx->ccache_file_name = "FILE:" CCACHE_PATH; + + kerr = krb5_cc_resolve(test_ctx->kctx, test_ctx->ccache_file_name, + &ccache); + assert_int_equal(kerr, 0); + + kerr = krb5_cc_initialize(test_ctx->kctx, ccache, test_creds.client); + assert_int_equal(kerr, 0); + + kerr = krb5_cc_store_cred(test_ctx->kctx, ccache, &test_creds); + assert_int_equal(kerr, 0); + + kerr = krb5_cc_close(test_ctx->kctx, ccache); + assert_int_equal(kerr, 0); + + check_leaks_push(test_ctx); + *state = test_ctx; + + krb5_free_authdata(test_ctx->kctx, test_creds.authdata); + return 0; +} + +static int teardown_ccache(void **state) +{ + int ret; + struct ccache_test_ctx *test_ctx = talloc_get_type(*state, + struct ccache_test_ctx); + assert_non_null(test_ctx); + + krb5_free_principal(test_ctx->kctx, test_ctx->client_principal); + krb5_free_principal(test_ctx->kctx, test_ctx->server_principal); + krb5_free_context(test_ctx->kctx); + + ret = unlink(CCACHE_PATH); + assert_int_equal(ret, 0); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void test_copy_ccache(void **state) +{ + krb5_error_code kerr; + char *mem_ccache_name; + krb5_ccache ccache; + krb5_creds mcreds; + krb5_creds creds; + krb5_principal mem_principal; + struct ccache_test_ctx *test_ctx = talloc_get_type(*state, + struct ccache_test_ctx); + assert_non_null(test_ctx); + + kerr = copy_ccache_into_memory(test_ctx, test_ctx->kctx, + test_ctx->ccache_file_name, + &mem_ccache_name); + assert_int_equal(kerr, 0); + assert_non_null(mem_ccache_name); + + kerr = krb5_cc_resolve(test_ctx->kctx, mem_ccache_name, &ccache); + assert_int_equal(kerr, 0); + + talloc_free(mem_ccache_name); + + kerr = krb5_cc_get_principal(test_ctx->kctx, ccache, &mem_principal); + assert_int_equal(kerr, 0); + assert_non_null(mem_principal); + + assert_true(krb5_principal_compare(test_ctx->kctx, mem_principal, + test_ctx->client_principal)); + krb5_free_principal(test_ctx->kctx, mem_principal); + + memset(&mcreds, 0, sizeof(mcreds)); + memset(&creds, 0, sizeof(mcreds)); + mcreds.client = test_ctx->client_principal; + mcreds.server = test_ctx->server_principal; + kerr = krb5_cc_retrieve_cred(test_ctx->kctx, ccache, 0, &mcreds, &creds); + assert_int_equal(kerr, 0); + krb5_free_cred_contents(test_ctx->kctx, &creds); + + kerr = krb5_cc_destroy(test_ctx->kctx, ccache); + assert_int_equal(kerr, 0); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_copy_ccache, + setup_ccache, teardown_ccache), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/cmocka/test_copy_keytab.c b/src/tests/cmocka/test_copy_keytab.c new file mode 100644 index 0000000..22e0b88 --- /dev/null +++ b/src/tests/cmocka/test_copy_keytab.c @@ -0,0 +1,324 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: Tests keytab utilities + + 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 <stdio.h> +#include <popt.h> + +#include "util/sss_krb5.h" +#include "providers/krb5/krb5_common.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_krb5.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define KEYTAB_TEST_PRINC "test/keytab@TEST.KEYTAB" +#define KEYTAB_PATH TESTS_PATH "/keytab_test.keytab" +#define EMPTY_KEYTAB_PATH TESTS_PATH "/empty_keytab_test.keytab" + +struct keytab_test_ctx { + krb5_context kctx; + const char *keytab_file_name; + krb5_principal principal; + krb5_enctype *enctypes; +}; + +static int setup_keytab(void **state) +{ + struct keytab_test_ctx *test_ctx; + krb5_error_code kerr; + size_t nkeys = 4; + krb5_keytab_entry keys[nkeys]; + + test_dom_suite_setup(TESTS_PATH); + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct keytab_test_ctx); + assert_non_null(test_ctx); + + kerr = krb5_init_context(&test_ctx->kctx); + assert_int_equal(kerr, 0); + + test_ctx->keytab_file_name = "FILE:" KEYTAB_PATH; + + kerr = krb5_parse_name(test_ctx->kctx, KEYTAB_TEST_PRINC, + &test_ctx->principal); + assert_int_equal(kerr, 0); + + kerr = krb5_get_permitted_enctypes(test_ctx->kctx, &test_ctx->enctypes); + assert_int_equal(kerr, 0); + /* We need at least 2 different encryption types to properly test + * the selection of keys. */ + assert_int_not_equal(test_ctx->enctypes[0], 0); + assert_int_not_equal(test_ctx->enctypes[1], 0); + + + memset(&keys, nkeys, nkeys * sizeof(krb5_keytab_entry)); + + mock_krb5_keytab_entry(&keys[0], test_ctx->principal, 12345, 1, + test_ctx->enctypes[0], "11"); + mock_krb5_keytab_entry(&keys[1], test_ctx->principal, 12345, 1, + test_ctx->enctypes[1], "12"); + mock_krb5_keytab_entry(&keys[2], test_ctx->principal, 12345, 2, + test_ctx->enctypes[0], "21"); + mock_krb5_keytab_entry(&keys[3], test_ctx->principal, 12345, 2, + test_ctx->enctypes[1], "22"); + + kerr = mock_keytab(test_ctx->kctx, test_ctx->keytab_file_name, keys, nkeys); + assert_int_equal(kerr, 0); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int teardown_keytab(void **state) +{ + int ret; + struct keytab_test_ctx *test_ctx = talloc_get_type(*state, + struct keytab_test_ctx); + assert_non_null(test_ctx); + + krb5_free_enctypes(test_ctx->kctx, test_ctx->enctypes); + krb5_free_principal(test_ctx->kctx, test_ctx->principal); + krb5_free_context(test_ctx->kctx); + + ret = unlink(KEYTAB_PATH); + assert_int_equal(ret, 0); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + + ret = rmdir(TESTS_PATH); + assert_return_code(ret, errno); + + return 0; +} + +void test_copy_keytab(void **state) +{ + krb5_error_code kerr; + char *mem_keytab_name; + krb5_keytab mem_keytab; + krb5_keytab keytab; + krb5_keytab_entry kent; + struct keytab_test_ctx *test_ctx = talloc_get_type(*state, + struct keytab_test_ctx); + assert_non_null(test_ctx); + + kerr = copy_keytab_into_memory(test_ctx, test_ctx->kctx, + test_ctx->keytab_file_name, + &mem_keytab_name, &mem_keytab); + assert_int_equal(kerr, 0); + assert_non_null(mem_keytab_name); + + kerr = krb5_kt_resolve(test_ctx->kctx, mem_keytab_name, &keytab); + assert_int_equal(kerr, 0); + + kerr = krb5_kt_get_entry(test_ctx->kctx, keytab, test_ctx->principal, 9, 9, + &kent); + assert_int_not_equal(kerr, 0); + + kerr = krb5_kt_get_entry(test_ctx->kctx, keytab, test_ctx->principal, 1, + test_ctx->enctypes[0], &kent); + assert_int_equal(kerr, 0); + krb5_free_keytab_entry_contents(test_ctx->kctx, &kent); + + kerr = krb5_kt_get_entry(test_ctx->kctx, keytab, test_ctx->principal, 1, + test_ctx->enctypes[1], &kent); + assert_int_equal(kerr, 0); + krb5_free_keytab_entry_contents(test_ctx->kctx, &kent); + + kerr = krb5_kt_get_entry(test_ctx->kctx, keytab, test_ctx->principal, 2, + test_ctx->enctypes[0], &kent); + assert_int_equal(kerr, 0); + krb5_free_keytab_entry_contents(test_ctx->kctx, &kent); + + kerr = krb5_kt_get_entry(test_ctx->kctx, keytab, test_ctx->principal, 2, + test_ctx->enctypes[1], &kent); + assert_int_equal(kerr, 0); + krb5_free_keytab_entry_contents(test_ctx->kctx, &kent); + + talloc_free(mem_keytab_name); + + kerr = krb5_kt_close(test_ctx->kctx, keytab); + assert_int_equal(kerr, 0); + + kerr = krb5_kt_close(test_ctx->kctx, mem_keytab); + assert_int_equal(kerr, 0); +} + +void test_sss_krb5_kt_have_content(void **state) +{ + krb5_error_code kerr; + krb5_keytab keytab; + struct keytab_test_ctx *test_ctx = talloc_get_type(*state, + struct keytab_test_ctx); + assert_non_null(test_ctx); + + kerr = krb5_kt_resolve(test_ctx->kctx, test_ctx->keytab_file_name, &keytab); + assert_int_equal(kerr, 0); + + kerr = sss_krb5_kt_have_content(test_ctx->kctx, keytab); + assert_int_equal(kerr, 0); + + kerr = krb5_kt_close(test_ctx->kctx, keytab); + assert_int_equal(kerr, 0); + + kerr = krb5_kt_resolve(test_ctx->kctx, "FILE:" EMPTY_KEYTAB_PATH, &keytab); + assert_int_equal(kerr, 0); + + kerr = sss_krb5_kt_have_content(test_ctx->kctx, keytab); + assert_int_equal(kerr, KRB5_KT_NOTFOUND); + + kerr = krb5_kt_close(test_ctx->kctx, keytab); + assert_int_equal(kerr, 0); + + /* no need to remove EMPTY_KEYTAB_PATH because krb5_kt_close() does not + * create empty keytab files */ +} + +static bool keytab_entries_equal(krb5_keytab_entry kent1, + krb5_keytab_entry kent2) +{ + if (kent1.vno != kent2.vno + || kent1.key.enctype != kent2.key.enctype + || kent1.key.length != kent2.key.length + || memcmp(kent1.key.contents, kent2.key.contents, + kent1.key.length) != 0 ) { + return false; + } + + return true; +} + +void test_copy_keytab_order(void **state) +{ + krb5_error_code kerr; + krb5_error_code kerr_mem; + char *mem_keytab_name; + krb5_keytab mem_keytab; + krb5_kt_cursor mem_cursor; + krb5_keytab_entry mem_kent; + krb5_keytab keytab; + krb5_kt_cursor cursor; + krb5_keytab_entry kent; + struct keytab_test_ctx *test_ctx = talloc_get_type(*state, + struct keytab_test_ctx); + assert_non_null(test_ctx); + + kerr = copy_keytab_into_memory(test_ctx, test_ctx->kctx, + test_ctx->keytab_file_name, + &mem_keytab_name, &mem_keytab); + assert_int_equal(kerr, 0); + assert_non_null(mem_keytab_name); + + kerr = krb5_kt_resolve(test_ctx->kctx, mem_keytab_name, &mem_keytab); + assert_int_equal(kerr, 0); + + kerr = krb5_kt_resolve(test_ctx->kctx, test_ctx->keytab_file_name, &keytab); + assert_int_equal(kerr, 0); + + kerr = krb5_kt_start_seq_get(test_ctx->kctx, mem_keytab, &mem_cursor); + assert_int_equal(kerr, 0); + + kerr = krb5_kt_start_seq_get(test_ctx->kctx, keytab, &cursor); + assert_int_equal(kerr, 0); + + while ((kerr = krb5_kt_next_entry(test_ctx->kctx, keytab, &kent, + &cursor)) == 0) { + kerr_mem = krb5_kt_next_entry(test_ctx->kctx, mem_keytab, &mem_kent, + &mem_cursor); + assert_int_equal(kerr_mem, 0); + + assert_true(keytab_entries_equal(kent, mem_kent)); + + krb5_free_keytab_entry_contents(test_ctx->kctx, &kent); + krb5_free_keytab_entry_contents(test_ctx->kctx, &mem_kent); + } + + assert_int_equal(kerr, KRB5_KT_END); + + kerr_mem = krb5_kt_next_entry(test_ctx->kctx, mem_keytab, &mem_kent, + &mem_cursor); + assert_int_equal(kerr_mem, KRB5_KT_END); + + kerr = krb5_kt_end_seq_get(test_ctx->kctx, mem_keytab, &mem_cursor); + assert_int_equal(kerr, 0); + + kerr = krb5_kt_end_seq_get(test_ctx->kctx, keytab, &cursor); + assert_int_equal(kerr, 0); + + talloc_free(mem_keytab_name); + + kerr = krb5_kt_close(test_ctx->kctx, keytab); + assert_int_equal(kerr, 0); + + kerr = krb5_kt_close(test_ctx->kctx, mem_keytab); + assert_int_equal(kerr, 0); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_copy_keytab, + setup_keytab, teardown_keytab), + cmocka_unit_test_setup_teardown(test_sss_krb5_kt_have_content, + setup_keytab, teardown_keytab), + cmocka_unit_test_setup_teardown(test_copy_keytab_order, + setup_keytab, teardown_keytab), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/cmocka/test_data_provider_be.c b/src/tests/cmocka/test_data_provider_be.c new file mode 100644 index 0000000..49f04dd --- /dev/null +++ b/src/tests/cmocka/test_data_provider_be.c @@ -0,0 +1,259 @@ +/* + Copyright (C) 2015 Red Hat + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <time.h> + +#include "providers/backend.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_be.h" +#include "tests/common.h" + +#define TESTS_PATH "tests_dp_be" +#define TEST_CONF_DB "test_dp_be_conf.ldb" +#define TEST_DOM_NAME "dp_be_test" +#define TEST_ID_PROVIDER "ldap" + +#define OFFLINE_TIMEOUT 2 +#define STR_HELPER(x) #x +#define AS_STR(param) STR_HELPER(param) + +static TALLOC_CTX *global_mock_context = NULL; +static bool global_timer_added; + +struct tevent_timer *__real__tevent_add_timer(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + struct timeval next_event, + tevent_timer_handler_t handler, + void *private_data, + const char *handler_name, + const char *location); + +struct tevent_timer *__wrap__tevent_add_timer(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + struct timeval next_event, + tevent_timer_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + global_timer_added = true; + + return __real__tevent_add_timer(ev, mem_ctx, next_event, + handler, private_data, handler_name, + location); +} + + +struct test_ctx { + struct sss_test_ctx *tctx; + struct be_ctx *be_ctx; +}; + +static int test_setup(void **state) +{ + struct test_ctx *test_ctx = NULL; + struct sss_test_conf_param params[] = { + { "offline_timeout", AS_STR(OFFLINE_TIMEOUT) }, + { NULL, NULL }, /* Sentinel */ + }; + + assert_true(leak_check_setup()); + global_mock_context = talloc_new(global_talloc_context); + assert_non_null(global_mock_context); + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, params); + assert_non_null(test_ctx->tctx); + + test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx); + assert_non_null(test_ctx->be_ctx); + + test_ctx->be_ctx->domain->subdomains = named_domain(test_ctx, + "subdomains", + test_ctx->be_ctx->domain); + assert_non_null(test_ctx->be_ctx->domain->subdomains); + + *state = test_ctx; + + return 0; +} + +static int test_teardown(void **state) +{ + talloc_zfree(*state); + assert_true(leak_check_teardown()); + return 0; +} + +static void assert_domain_state(struct sss_domain_info *dom, + enum sss_domain_state expected_state) +{ + enum sss_domain_state dom_state; + + dom_state = sss_domain_get_state(dom); + assert_int_equal(dom_state, expected_state); +} + +static void test_mark_subdom_offline_check(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt) +{ + struct test_ctx *test_ctx = talloc_get_type(pvt, struct test_ctx); + + assert_domain_state(test_ctx->be_ctx->domain->subdomains, + DOM_ACTIVE); + + test_ctx->tctx->done = true; + test_ctx->tctx->error = EOK; +} + +static void test_mark_dom_offline(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_domain_state(test_ctx->be_ctx->domain, DOM_ACTIVE); + assert_false(be_is_offline(test_ctx->be_ctx)); + + be_mark_dom_offline(test_ctx->be_ctx->domain, test_ctx->be_ctx); + + assert_true(be_is_offline(test_ctx->be_ctx)); + assert_domain_state(test_ctx->be_ctx->domain, DOM_ACTIVE); +} + +static void test_mark_subdom_offline(void **state) +{ + struct timeval tv; + struct tevent_timer *check_ev = NULL; + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + errno_t ret; + + assert_domain_state(test_ctx->be_ctx->domain->subdomains, + DOM_ACTIVE); + assert_false(be_is_offline(test_ctx->be_ctx)); + + global_timer_added = false; + be_mark_dom_offline(test_ctx->be_ctx->domain->subdomains, test_ctx->be_ctx); + assert_domain_state(test_ctx->be_ctx->domain->subdomains, + DOM_INACTIVE); + + /* A timer must be added that resets the state back */ + assert_true(global_timer_added); + + /* Global offline state must not change */ + assert_false(be_is_offline(test_ctx->be_ctx)); + + /* Make sure we don't add a second timer */ + global_timer_added = false; + be_mark_dom_offline(test_ctx->be_ctx->domain->subdomains, test_ctx->be_ctx); + assert_domain_state(test_ctx->be_ctx->domain->subdomains, + DOM_INACTIVE); + assert_false(global_timer_added); + + /* Wait for the internal timer to reset our subdomain back */ + tv = tevent_timeval_current_ofs(OFFLINE_TIMEOUT + 1, 0); + + check_ev = tevent_add_timer(test_ctx->tctx->ev, test_ctx, tv, + test_mark_subdom_offline_check, + test_ctx); + if (check_ev == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot create timer\n"); + return; + } + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static void test_mark_subdom_offline_disabled(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + + sss_domain_set_state(test_ctx->be_ctx->domain->subdomains, DOM_DISABLED); + assert_domain_state(test_ctx->be_ctx->domain->subdomains, + DOM_DISABLED); + + be_mark_dom_offline(test_ctx->be_ctx->domain->subdomains, test_ctx->be_ctx); + assert_domain_state(test_ctx->be_ctx->domain->subdomains, + DOM_DISABLED); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + int no_cleanup = 0; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_mark_dom_offline, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_mark_subdom_offline, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_mark_subdom_offline_disabled, + test_setup, + test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + return rv; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_deskprofile_utils.c b/src/tests/cmocka/test_deskprofile_utils.c new file mode 100644 index 0000000..f52741b --- /dev/null +++ b/src/tests/cmocka/test_deskprofile_utils.c @@ -0,0 +1,162 @@ +/* + Authors: + Fabiano Fidテェncio <fidencio@redhat.com> + + Copyright (C) 2018 Red Hat + + SSSD tests: Tests for desktop profile utilities functions + + 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/>. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "src/providers/ipa/ipa_deskprofile_rules_util.h" + +#define RULES_DIR "/var/lib/sss/deskprofile" +#define DOMAIN "domain.example" +#define USERNAME "user" +#define PRIO "000420" +#define RULE_NAME "rule" +#define EXTENSION "json" +#define USER "000420" +#define GROUP "000000" +#define HOST "000000" +#define HOSTGROUP "000420" + +void test_deskprofile_get_filename_path(void **state) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret; + char *result = NULL; + const char *results[24]; + + /* All the results are based as: + * user and hostgroup match the rules; + * group and host don't match the rules; + */ + + /* 1 = user, group, host, hostgroup */ + results[0] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"USER"_"GROUP"_"HOST"_"HOSTGROUP"_"RULE_NAME"."EXTENSION; + /* 2 = user, group, hostgroup, host */ + results[1] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"USER"_"GROUP"_"HOSTGROUP"_"HOST"_"RULE_NAME"."EXTENSION; + /* 3 = user, host, group, hostgroup */ + results[2] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"USER"_"HOST"_"GROUP"_"HOSTGROUP"_"RULE_NAME"."EXTENSION; + /* 4 = user, host, hostgroup, group */ + results[3] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"USER"_"HOST"_"HOSTGROUP"_"GROUP"_"RULE_NAME"."EXTENSION; + /* 5 = user, hostgroup, group, host */ + results[4] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"USER"_"HOSTGROUP"_"GROUP"_"HOST"_"RULE_NAME"."EXTENSION; + /* 6 = user, hostgroup, host, group */ + results[5] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"USER"_"HOSTGROUP"_"HOST"_"GROUP"_"RULE_NAME"."EXTENSION; + /* 7 = group, user, host, hostgroup */ + results[6] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"GROUP"_"USER"_"HOST"_"HOSTGROUP"_"RULE_NAME"."EXTENSION; + /* 8 = group, user, hostgroup, host */ + results[7] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"GROUP"_"USER"_"HOSTGROUP"_"HOST"_"RULE_NAME"."EXTENSION; + /* 9 = group, host, user, hostgroup */ + results[8] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"GROUP"_"HOST"_"USER"_"HOSTGROUP"_"RULE_NAME"."EXTENSION; + /* 10 = group, host, hostgroup, user */ + results[9] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"GROUP"_"HOST"_"HOSTGROUP"_"USER"_"RULE_NAME"."EXTENSION; + /* 11 = group, hostgroup, user, host */ + results[10] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"GROUP"_"HOSTGROUP"_"USER"_"HOST"_"RULE_NAME"."EXTENSION; + /* 12 = group, hostgroup, host, user */ + results[11] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"GROUP"_"HOSTGROUP"_"HOST"_"USER"_"RULE_NAME"."EXTENSION; + /* 13 = host, user, group, hostgroup */ + results[12] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOST"_"USER"_"GROUP"_"HOSTGROUP"_"RULE_NAME"."EXTENSION; + /* 14 = host, user, hostgroup, group */ + results[13] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOST"_"USER"_"HOSTGROUP"_"GROUP"_"RULE_NAME"."EXTENSION; + /* 15 = host, group, user, hostgroup */ + results[14] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOST"_"GROUP"_"USER"_"HOSTGROUP"_"RULE_NAME"."EXTENSION; + /* 16 = host, group, hostgroup, user */ + results[15] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOST"_"GROUP"_"HOSTGROUP"_"USER"_"RULE_NAME"."EXTENSION; + /* 17 = host, hostgroup, user, group */ + results[16] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOST"_"HOSTGROUP"_"USER"_"GROUP"_"RULE_NAME"."EXTENSION; + /* 18 = host, hostgroup, group, user */ + results[17] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOST"_"HOSTGROUP"_"GROUP"_"USER"_"RULE_NAME"."EXTENSION; + /* 19 = hostgroup, user, group, host */ + results[18] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOSTGROUP"_"USER"_"GROUP"_"HOST"_"RULE_NAME"."EXTENSION; + /* 20 = hostgroup, user, host, group */ + results[19] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOSTGROUP"_"USER"_"HOST"_"GROUP"_"RULE_NAME"."EXTENSION; + /* 21 = hostgroup, group, user, host */ + results[20] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOSTGROUP"_"GROUP"_"USER"_"HOST"_"RULE_NAME"."EXTENSION; + /* 22 = hostgroup, group, host, user */ + results[21] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOSTGROUP"_"GROUP"_"HOST"_"USER"_"RULE_NAME"."EXTENSION; + /* 23 = hostgroup, host, user, group */ + results[22] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOSTGROUP"_"HOST"_"USER"_"GROUP"_"RULE_NAME"."EXTENSION; + /* 24 = hostgroup, host, group, user */ + results[23] = RULES_DIR"/"DOMAIN"/"USERNAME"/"PRIO"_"HOSTGROUP"_"HOST"_"GROUP"_"USER"_"RULE_NAME"."EXTENSION; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + + for (uint16_t i = 0; i < 24; i++) { + ret = ipa_deskprofile_get_filename_path(tmp_ctx, + i + 1, + RULES_DIR, + DOMAIN, + USERNAME, + PRIO, + USER, + GROUP, + HOST, + HOSTGROUP, + RULE_NAME, + EXTENSION, + &result); + assert_int_equal(ret, EOK); + assert_string_equal(results[i], result); + + talloc_zfree(result); + } + + talloc_free(tmp_ctx); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_deskprofile_get_filename_path), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + return rv; +} diff --git a/src/tests/cmocka/test_domain_resolution_order.c b/src/tests/cmocka/test_domain_resolution_order.c new file mode 100644 index 0000000..e0a9fce --- /dev/null +++ b/src/tests/cmocka/test_domain_resolution_order.c @@ -0,0 +1,234 @@ +/* + Authors: + Fabiano Fidテェncio <fidencio@redhat.com> + + Copyright (C) 2018 Red Hat + + SSSD tests: Tests for domain resolution order functions + + 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/>. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "responder/common/cache_req/cache_req_domain.h" + +#define DOM_COUNT 3 +#define DOMAIN_1 "one.domain.test" +#define DOMAIN_2 "two.domain.test" +#define DOMAIN_3 "three.domain.test" +#define DOMAIN_RESOLUTION_ORDER DOMAIN_2":"DOMAIN_1 +#define LDAP "ldap" +#define FILES "files" + +struct domain_resolution_order_test_ctx { + size_t dom_count; + struct sss_domain_info *dom_list; +}; + +static void test_domain_resolution_order(void **state) +{ + struct domain_resolution_order_test_ctx *test_ctx; + struct cache_req_domain *cr_domains = NULL; + struct cache_req_domain *cr_domain; + const char *expected_order[DOM_COUNT] = { DOMAIN_2, DOMAIN_1, DOMAIN_3 }; + errno_t ret; + size_t c; + + test_ctx = talloc_get_type(*state, + struct domain_resolution_order_test_ctx); + + cr_domains = talloc_zero(test_ctx, struct cache_req_domain); + ret = cache_req_domain_new_list_from_domain_resolution_order( + test_ctx, + test_ctx->dom_list, + DOMAIN_RESOLUTION_ORDER, + &cr_domains); + assert_int_equal(ret, EOK); + + for (c = 0, cr_domain = cr_domains; cr_domain != NULL; + cr_domain = cr_domain->next, c++) { + assert_string_equal(expected_order[c], cr_domain->domain->name); + } +} + +#ifdef BUILD_FILES_PROVIDER +static void +test_domain_resolution_order_with_implicit_files_provider(void **state) +{ + struct domain_resolution_order_test_ctx *test_ctx; + struct cache_req_domain *cr_domains = NULL; + struct cache_req_domain *cr_domain; + const char *expected_order[DOM_COUNT] = { DOMAIN_3, DOMAIN_2, DOMAIN_1 }; + errno_t ret; + size_t c; + + test_ctx = talloc_get_type(*state, + struct domain_resolution_order_test_ctx); + + cr_domains = talloc_zero(test_ctx, struct cache_req_domain); + ret = cache_req_domain_new_list_from_domain_resolution_order( + test_ctx, + test_ctx->dom_list, + DOMAIN_RESOLUTION_ORDER, + &cr_domains); + assert_int_equal(ret, EOK); + + for (c = 0, cr_domain = cr_domains; cr_domain != NULL; + cr_domain = cr_domain->next, c++) { + assert_string_equal(expected_order[c], cr_domain->domain->name); + } +} +#endif + +static void test_domain_resolution_order_output_fqnames(void **state) +{ + struct domain_resolution_order_test_ctx *test_ctx; + struct cache_req_domain *cr_domains = NULL; + struct cache_req_domain *cr_domain; + errno_t ret; + + test_ctx = talloc_get_type(*state, + struct domain_resolution_order_test_ctx); + + cr_domains = talloc_zero(test_ctx, struct cache_req_domain); + ret = cache_req_domain_new_list_from_domain_resolution_order( + test_ctx, + test_ctx->dom_list, + DOMAIN_RESOLUTION_ORDER, + &cr_domains); + assert_int_equal(ret, EOK); + + for (cr_domain = cr_domains; cr_domain != NULL; + cr_domain = cr_domain->next) { + struct sss_domain_info *dom = cr_domain->domain; + bool expected = !is_files_provider(dom); + bool output_fqnames = sss_domain_info_get_output_fqnames(dom); + + assert_true(expected == output_fqnames); + } +} + +static int setup_domains_list_helper(void **state, bool with_files_provider) +{ + struct domain_resolution_order_test_ctx *test_ctx; + struct sss_domain_info *dom = NULL; + const char *domains[DOM_COUNT] = { DOMAIN_1, DOMAIN_2, DOMAIN_3 }; + const char *providers[DOM_COUNT] = { LDAP, LDAP, LDAP }; + size_t c; + + if (with_files_provider) { + providers[DOM_COUNT - 1] = FILES; + } + + test_ctx = talloc_zero(global_talloc_context, + struct domain_resolution_order_test_ctx); + assert_non_null(test_ctx); + + test_ctx->dom_count = DOM_COUNT; + + for (c = 0; c < test_ctx->dom_count; c++) { + dom = talloc_zero(test_ctx, struct sss_domain_info); + assert_non_null(dom); + + dom->name = talloc_strdup(dom, domains[c]); + assert_non_null(dom->name); + + dom->provider = talloc_strdup(dom, providers[c]); + assert_non_null(dom->provider); + + DLIST_ADD(test_ctx->dom_list, dom); + } + + *state = test_ctx; + return 0; +} + +static int setup_domains_list(void **state) +{ + return setup_domains_list_helper(state, false); +} + +#ifdef BUILD_FILES_PROVIDER +static int setup_domains_list_with_implicit_files_provider(void **state) +{ + return setup_domains_list_helper(state, true); +} +#endif + +static int teardown_domains_list(void **state) +{ + struct domain_resolution_order_test_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, + struct domain_resolution_order_test_ctx); + if (test_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return 1; + } + + talloc_free(test_ctx); + return 0; +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_domain_resolution_order, + setup_domains_list, + teardown_domains_list), +#ifdef BUILD_FILES_PROVIDER + cmocka_unit_test_setup_teardown( + test_domain_resolution_order_with_implicit_files_provider, + setup_domains_list_with_implicit_files_provider, + teardown_domains_list), +#endif + cmocka_unit_test_setup_teardown( + test_domain_resolution_order_output_fqnames, + setup_domains_list, + teardown_domains_list), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + return rv; +} diff --git a/src/tests/cmocka/test_dp_opts.c b/src/tests/cmocka/test_dp_opts.c new file mode 100644 index 0000000..8ef7125 --- /dev/null +++ b/src/tests/cmocka/test_dp_opts.c @@ -0,0 +1,530 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: Data Provider Option Tests + + 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 <popt.h> + +#include "providers/data_provider.h" + +#include "tests/cmocka/common_mock.h" + +#define STRING_DEFAULT "stringval" +#define BLOB_DEFAULT "blobval" +#define INT_DEFAULT 123 + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_opt_conf.ldb" +#define TEST_DOM_NAME "opt_test" +#define TEST_ID_PROVIDER "ldap" + +enum test_opts { + OPT_STRING_NODEFAULT, + OPT_STRING_DEFAULT, + OPT_BLOB_NODEFAULT, + OPT_BLOB_DEFAULT, + OPT_INT_NODEFAULT, + OPT_INT_DEFAULT, + OPT_BOOL_TRUE, + OPT_BOOL_FALSE, + + OPT_NUM_OPTS +}; + +struct dp_option test_def_opts[] = { + { "string_nodefault", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "string_default", DP_OPT_STRING, { STRING_DEFAULT }, NULL_STRING}, + { "blob_nodefault", DP_OPT_BLOB, NULL_BLOB, NULL_BLOB }, + { "blob_default", DP_OPT_BLOB, + { .blob = { discard_const(BLOB_DEFAULT), + sizeof(BLOB_DEFAULT) - 1 } }, + NULL_BLOB }, + { "int_nodefault", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER }, + { "int_default", DP_OPT_NUMBER, { .number = INT_DEFAULT }, NULL_NUMBER }, + { "bool_true", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, + { "bool_false", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, + DP_OPTION_TERMINATOR +}; + +static void assert_defaults(struct dp_option *opts) +{ + char *s; + struct dp_opt_blob b; + int i; + bool bo; + + s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT); + assert_null(s); + + s = dp_opt_get_string(opts, OPT_STRING_DEFAULT); + assert_non_null(s); + assert_string_equal(s, STRING_DEFAULT); + + b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT); + assert_null(b.data); + assert_int_equal(b.length, 0); + + b = dp_opt_get_blob(opts, OPT_BLOB_DEFAULT); + assert_non_null(b.data); + assert_int_equal(b.length, strlen(BLOB_DEFAULT)); + assert_memory_equal(b.data, BLOB_DEFAULT, strlen(BLOB_DEFAULT)); + + i = dp_opt_get_int(opts, OPT_INT_NODEFAULT); + assert_int_equal(i, 0); + + i = dp_opt_get_int(opts, OPT_INT_DEFAULT); + assert_int_equal(i, INT_DEFAULT); + + bo = dp_opt_get_bool(opts, OPT_BOOL_TRUE); + assert_true(bo == true); + + bo = dp_opt_get_bool(opts, OPT_BOOL_FALSE); + assert_true(bo == false); +} + +void opt_test_copy_default(void **state) +{ + int ret; + TALLOC_CTX *mem_ctx; + struct dp_option *opts; + struct dp_opt_blob b; + + mem_ctx = talloc_new(global_talloc_context); + assert_non_null(mem_ctx); + + ret = dp_copy_defaults(mem_ctx, test_def_opts, OPT_NUM_OPTS, &opts); + assert_int_equal(ret, EOK); + assert_defaults(opts); + + /* Test that copy_defaults would still copy defaults even if we + * change the values + */ + ret = dp_opt_set_string(opts, OPT_STRING_NODEFAULT, "str1"); + assert_int_equal(ret, EOK); + ret = dp_opt_set_string(opts, OPT_STRING_DEFAULT, "str2"); + assert_int_equal(ret, EOK); + + b.data = discard_const_p(uint8_t, "blob1"); + b.length = strlen("blob1"); + ret = dp_opt_set_blob(opts, OPT_BLOB_NODEFAULT, b); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_blob(opts, OPT_BLOB_DEFAULT, b); + b.data = discard_const_p(uint8_t, "blob2"); + b.length = strlen("blob2"); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_int(opts, OPT_INT_NODEFAULT, 456); + assert_int_equal(ret, EOK); + ret = dp_opt_set_int(opts, OPT_INT_DEFAULT, 789); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_bool(opts, OPT_BOOL_TRUE, false); + assert_int_equal(ret, EOK); + ret = dp_opt_set_bool(opts, OPT_BOOL_FALSE, true); + assert_int_equal(ret, EOK); + + talloc_free(opts); + ret = dp_copy_defaults(mem_ctx, test_def_opts, OPT_NUM_OPTS, &opts); + assert_int_equal(ret, EOK); + assert_defaults(opts); +} + +void opt_test_copy_options(void **state) +{ + int ret; + TALLOC_CTX *mem_ctx; + struct dp_option *opts; + char *s; + struct dp_opt_blob b; + int i; + bool bo; + + mem_ctx = talloc_new(global_talloc_context); + assert_non_null(mem_ctx); + + ret = dp_copy_options(mem_ctx, test_def_opts, OPT_NUM_OPTS, &opts); + assert_int_equal(ret, EOK); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_string(opts, OPT_STRING_NODEFAULT, "str1"); + assert_int_equal(ret, EOK); + + b.data = discard_const_p(uint8_t, "blob1"); + b.length = strlen("blob1"); + ret = dp_opt_set_blob(opts, OPT_BLOB_NODEFAULT, b); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_int(opts, OPT_INT_NODEFAULT, 456); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_bool(opts, OPT_BOOL_TRUE, false); + assert_int_equal(ret, EOK); + + /* Test that options set to an explicit value retain + * the value and even options with default value + * do not return the default unless explicitly set + */ + s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT); + assert_string_equal(s, "str1"); + s = dp_opt_get_string(opts, OPT_STRING_DEFAULT); + assert_null(s); + + b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT); + assert_non_null(b.data); + assert_int_equal(b.length, strlen("blob1")); + assert_memory_equal(b.data, "blob1", strlen("blob1")); + b = dp_opt_get_blob(opts, OPT_BLOB_DEFAULT); + assert_null(b.data); + assert_int_equal(b.length, 0); + + i = dp_opt_get_int(opts, OPT_INT_NODEFAULT); + assert_int_equal(i, 456); + i = dp_opt_get_int(opts, OPT_INT_DEFAULT); + assert_int_equal(i, 0); + + bo = dp_opt_get_bool(opts, OPT_BOOL_TRUE); + assert_false(bo == true); +} + +void opt_test_get(void **state) +{ + int ret; + struct sss_test_ctx *tctx; + struct dp_option *opts; + struct sss_test_conf_param params[] = { + { "string_nodefault", "stringval2" }, + { "blob_nodefault", "blobval2" }, + { "int_nodefault", "456" }, + { "bool_true", "false" }, + { NULL, NULL }, /* Sentinel */ + }; + char *s; + struct dp_opt_blob b; + int i; + bool bo; + + tctx = create_dom_test_ctx(global_talloc_context, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, TEST_ID_PROVIDER, params); + assert_non_null(tctx); + + ret = dp_get_options(tctx, tctx->confdb, tctx->conf_dom_path, + test_def_opts, OPT_NUM_OPTS, &opts); + assert_int_equal(ret, EOK); + + /* Options that were not specified explicitly should only have the default + * value, those that have been specified explicitly should carry that + * value + */ + s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT); + assert_non_null(s); + assert_string_equal(s, "stringval2"); + + s = dp_opt_get_string(opts, OPT_STRING_DEFAULT); + assert_non_null(s); + assert_string_equal(s, STRING_DEFAULT); + + b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT); + assert_non_null(b.data); + assert_int_equal(b.length, strlen("blobval2")); + assert_memory_equal(b.data, "blobval2", strlen("blobval2")); + + b = dp_opt_get_blob(opts, OPT_BLOB_DEFAULT); + assert_non_null(b.data); + assert_int_equal(b.length, strlen(BLOB_DEFAULT)); + assert_memory_equal(b.data, BLOB_DEFAULT, strlen(BLOB_DEFAULT)); + + i = dp_opt_get_int(opts, OPT_INT_NODEFAULT); + assert_int_equal(i, 456); + + i = dp_opt_get_int(opts, OPT_INT_DEFAULT); + assert_int_equal(i, INT_DEFAULT); + + bo = dp_opt_get_bool(opts, OPT_BOOL_TRUE); + assert_true(bo == false); + + bo = dp_opt_get_bool(opts, OPT_BOOL_FALSE); + assert_true(bo == false); + + talloc_free(tctx); +} + +static int opt_test_getset_setup(void **state) +{ + int ret; + struct dp_option *opts; + + ret = dp_copy_defaults(global_talloc_context, + test_def_opts, OPT_NUM_OPTS, &opts); + assert_int_equal(ret, EOK); + assert_defaults(opts); + + *state = opts; + return 0; +} + +static int opt_test_getset_teardown(void **state) +{ + struct dp_option *opts = talloc_get_type(*state, struct dp_option); + talloc_free(opts); + return 0; +} + +static void assert_nondefault_string_empty(struct dp_option *opts) +{ + char *s; + + s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT); + assert_null(s); +} + +static void set_nondefault_string(struct dp_option *opts) +{ + int ret; + + ret = dp_opt_set_string(opts, OPT_STRING_NODEFAULT, "str1"); + assert_int_equal(ret, EOK); +} + +static void check_nondefault_string(struct dp_option *opts) +{ + char *s; + + s = dp_opt_get_string(opts, OPT_STRING_NODEFAULT); + assert_non_null(s); + assert_string_equal(s, "str1"); +} + +void opt_test_getset_string(void **state) +{ + struct dp_option *opts = talloc_get_type(*state, struct dp_option); + + assert_nondefault_string_empty(opts); + set_nondefault_string(opts); + check_nondefault_string(opts); +} + +static void assert_nondefault_blob_empty(struct dp_option *opts) +{ + struct dp_opt_blob b; + + b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT); + assert_null(b.data); + assert_int_equal(b.length, 0); +} + +static void set_nondefault_blob(struct dp_option *opts) +{ + struct dp_opt_blob b; + int ret; + + b.data = discard_const_p(uint8_t, "blob2"); + b.length = strlen("blob2"); + ret = dp_opt_set_blob(opts, OPT_BLOB_NODEFAULT, b); + assert_int_equal(ret, EOK); +} + +static void check_nondefault_blob(struct dp_option *opts) +{ + struct dp_opt_blob b; + + b = dp_opt_get_blob(opts, OPT_BLOB_NODEFAULT); + assert_non_null(b.data); + assert_int_equal(b.length, strlen("blob2")); + assert_memory_equal(b.data, "blob2", strlen("blob2")); +} + +void opt_test_getset_blob(void **state) +{ + struct dp_option *opts = talloc_get_type(*state, struct dp_option); + + assert_nondefault_blob_empty(opts); + set_nondefault_blob(opts); + check_nondefault_blob(opts); +} + +static void assert_nondefault_int_notset(struct dp_option *opts) +{ + int i; + i = dp_opt_get_int(opts, OPT_INT_NODEFAULT); + assert_int_equal(i, 0); +} + +static void set_nondefault_int(struct dp_option *opts) +{ + int ret; + ret = dp_opt_set_int(opts, OPT_INT_NODEFAULT, 456); + assert_int_equal(ret, EOK); +} + +static void assert_nondefault_int_set(struct dp_option *opts) +{ + int i; + i = dp_opt_get_int(opts, OPT_INT_NODEFAULT); + assert_int_equal(i, 456); +} + +void opt_test_getset_int(void **state) +{ + struct dp_option *opts = talloc_get_type(*state, struct dp_option); + + assert_nondefault_int_notset(opts); + set_nondefault_int(opts); + assert_nondefault_int_set(opts); +} + +void opt_test_getset_bool(void **state) +{ + struct dp_option *opts = talloc_get_type(*state, struct dp_option); + int ret; + bool b; + + b = dp_opt_get_bool(opts, OPT_BOOL_TRUE); + assert_true(b == true); + + ret = dp_opt_set_bool(opts, OPT_BOOL_TRUE, false); + assert_int_equal(ret, EOK); + + b = dp_opt_get_bool(opts, OPT_BOOL_TRUE); + assert_false(b == true); +} + +void opt_test_inherit(void **state) +{ + struct dp_option *opts = talloc_get_type(*state, struct dp_option); + int ret; + struct dp_option *opts_copy; + const char *s; + const char *sd_inherit_match[] = { "string_nodefault", + "blob_nodefault", + "int_nodefault", + "bool_true", + NULL }; + + ret = dp_copy_defaults(opts, test_def_opts, + OPT_NUM_OPTS, &opts_copy); + assert_int_equal(ret, EOK); + assert_defaults(opts); + + dp_option_inherit_match(NULL, OPT_STRING_NODEFAULT, + opts, opts_copy); + s = dp_opt_get_string(opts_copy, OPT_STRING_NODEFAULT); + assert_null(s); + + /* string */ + assert_nondefault_string_empty(opts_copy); + set_nondefault_string(opts); + dp_option_inherit_match(discard_const(sd_inherit_match), + OPT_STRING_NODEFAULT, + opts, opts_copy); + check_nondefault_string(opts_copy); + + /* blob */ + assert_nondefault_blob_empty(opts_copy); + set_nondefault_blob(opts); + dp_option_inherit_match(discard_const(sd_inherit_match), + OPT_BLOB_NODEFAULT, + opts, opts_copy); + check_nondefault_blob(opts_copy); + + /* number */ + assert_nondefault_int_notset(opts_copy); + set_nondefault_int(opts); + dp_option_inherit_match(discard_const(sd_inherit_match), + OPT_INT_NODEFAULT, + opts, opts_copy); + assert_nondefault_int_set(opts_copy); + + /* bool */ + assert_true(dp_opt_get_bool(opts_copy, OPT_BOOL_TRUE)); + + ret = dp_opt_set_bool(opts, OPT_BOOL_TRUE, false); + assert_int_equal(ret, EOK); + + dp_option_inherit_match(discard_const(sd_inherit_match), + OPT_BOOL_TRUE, + opts, opts_copy); + + assert_false(dp_opt_get_bool(opts_copy, OPT_BOOL_TRUE)); +} + +int main(int argc, const char *argv[]) +{ + int no_cleanup = 0; + poptContext pc; + int opt; + int ret; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(opt_test_getset_string, + opt_test_getset_setup, + opt_test_getset_teardown), + cmocka_unit_test_setup_teardown(opt_test_getset_int, + opt_test_getset_setup, + opt_test_getset_teardown), + cmocka_unit_test_setup_teardown(opt_test_getset_bool, + opt_test_getset_setup, + opt_test_getset_teardown), + cmocka_unit_test_setup_teardown(opt_test_getset_blob, + opt_test_getset_setup, + opt_test_getset_teardown), + cmocka_unit_test_setup_teardown(opt_test_inherit, + opt_test_getset_setup, + opt_test_getset_teardown), + cmocka_unit_test(opt_test_copy_default), + cmocka_unit_test(opt_test_copy_options), + cmocka_unit_test(opt_test_get) + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + ret = cmocka_run_group_tests(tests, NULL, NULL); + if (ret == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + return ret; +} diff --git a/src/tests/cmocka/test_dyndns.c b/src/tests/cmocka/test_dyndns.c new file mode 100644 index 0000000..7526c16 --- /dev/null +++ b/src/tests/cmocka/test_dyndns.c @@ -0,0 +1,1110 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: Dynamic DNS tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <unistd.h> +#include <sys/types.h> +#include <ifaddrs.h> +#include <arpa/inet.h> + +/* In order to access opaque types */ +#include "providers/be_dyndns.c" + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_be.h" +#include "src/providers/be_dyndns.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_dyndns_conf.ldb" +#define TEST_DOM_NAME "dyndns_test" +#define TEST_ID_PROVIDER "ldap" + +enum mock_nsupdate_states { + MOCK_NSUPDATE_OK, + MOCK_NSUPDATE_ERR, + MOCK_NSUPDATE_TIMEOUT, +}; + +static TALLOC_CTX *global_mock_context = NULL; + +struct dyndns_test_ctx { + struct sss_test_ctx *tctx; + + struct be_ctx *be_ctx; + struct be_nsupdate_ctx *update_ctx; + + enum mock_nsupdate_states state; + int child_status; + int child_retval; +}; + +static struct dyndns_test_ctx *dyndns_test_ctx; + +void __wrap_execv(const char *path, char *const argv[]) +{ + int err; + + switch (dyndns_test_ctx->state) { + case MOCK_NSUPDATE_OK: + DEBUG(SSSDBG_FUNC_DATA, "nsupdate success test case\n"); + err = 0; + usleep(50000); /* 50 milliseconds */ + break; + case MOCK_NSUPDATE_ERR: + DEBUG(SSSDBG_FUNC_DATA, "nsupdate error test case\n"); + err = 1; + usleep(50000); /* 50 milliseconds */ + break; + case MOCK_NSUPDATE_TIMEOUT: + DEBUG(SSSDBG_FUNC_DATA, "nsupdate timeout test case\n"); + err = 2; + sleep(3); + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "unknown test case\n"); + err = 255; + break; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Child exiting with status %d\n", err); + _exit(err); +} + +int __wrap_getifaddrs(struct ifaddrs **_ifap) +{ + struct ifaddrs *ifap = NULL; + struct ifaddrs *ifap_prev = NULL; + struct ifaddrs *ifap_head = NULL; + char *name; + char *straddr; + int ad_family; + struct sockaddr_in *sa; + void *dst; + + while ((name = sss_mock_ptr_type(char *)) != NULL) { + straddr = sss_mock_ptr_type(char *); + if (straddr == NULL) { + errno = EINVAL; + goto fail; + } + ad_family = sss_mock_type(int); + + ifap = talloc_zero(global_mock_context, struct ifaddrs); + if (ifap == NULL) { + errno = ENOMEM; /* getifaddrs sets errno, too */ + goto fail; + } + + if (ifap_prev) { + ifap_prev->ifa_next = ifap; + } else { + ifap_head = ifap; + } + ifap_prev = ifap; + + ifap->ifa_name = talloc_strdup(ifap, name); + if (ifap->ifa_name == NULL) { + errno = ENOMEM; + goto fail; + } + + /* Do not allocate directly on ifap->ifa_addr to + * avoid alignment warnings */ + if (ad_family == AF_INET) { + sa = talloc(ifap, struct sockaddr_in); + } else if (ad_family == AF_INET6) { + sa = (struct sockaddr_in *) talloc(ifap, struct sockaddr_in6); + } else { + errno = EINVAL; + goto fail; + } + + if (sa == NULL) { + errno = ENOMEM; + goto fail; + } + + sa->sin_family = ad_family; + + if (ad_family == AF_INET) { + dst = &sa->sin_addr; + } else if (ad_family == AF_INET6) { + dst = &((struct sockaddr_in6 *)sa)->sin6_addr; + } else { + errno = EINVAL; + goto fail; + } + + /* convert straddr into ifa_addr */ + if (inet_pton(ad_family, straddr, dst) != 1) { + goto fail; + } + + ifap->ifa_addr = (struct sockaddr *) sa; + } + + *_ifap = ifap_head; + return 0; + +fail: + talloc_free(ifap); + return -1; +} + +void __wrap_freeifaddrs(struct ifaddrs *ifap) +{ + talloc_free(ifap); +} + +static void dyndns_test_done(struct tevent_req *req) +{ + struct dyndns_test_ctx *ctx = + tevent_req_callback_data(req, struct dyndns_test_ctx); + + ctx->child_retval = -1; + ctx->tctx->error = be_nsupdate_recv(req, &ctx->child_status); + talloc_zfree(req); + + ctx->tctx->done = true; +} + +void will_return_getifaddrs(const char *ifname, const char *straddr, + int af_family) +{ + will_return(__wrap_getifaddrs, ifname); + if (ifname) { + will_return(__wrap_getifaddrs, straddr); + } + if (straddr) { + will_return(__wrap_getifaddrs, af_family); + } +} + +void dyndns_test_sss_iface_addr_get_misc(void **state) +{ + struct sss_iface_addr addrs[3]; + struct sockaddr ss[3]; + + addrs[0].prev = NULL; + addrs[0].next = &addrs[1]; + addrs[0].addr = &ss[0]; + addrs[1].prev = &addrs[0]; + addrs[1].next = &addrs[2]; + addrs[1].addr = &ss[1]; + addrs[2].prev = &addrs[1]; + addrs[2].next = NULL; + addrs[2].addr = &ss[2]; + + assert_ptr_equal(sss_iface_addr_get_address(NULL), NULL); + assert_ptr_equal(sss_iface_addr_get_address(&addrs[0]), &ss[0]); + assert_ptr_equal(sss_iface_addr_get_address(&addrs[1]), &ss[1]); + assert_ptr_equal(sss_iface_addr_get_address(&addrs[2]), &ss[2]); + + assert_ptr_equal(sss_iface_addr_get_next(NULL), NULL); + assert_ptr_equal(sss_iface_addr_get_next(&addrs[0]), &addrs[1]); + assert_ptr_equal(sss_iface_addr_get_next(&addrs[1]), &addrs[2]); + assert_ptr_equal(sss_iface_addr_get_next(&addrs[2]), NULL); +} + +void dyndns_test_get_ifaddr(void **state) +{ + errno_t ret; + struct sss_iface_addr *addrlist; + char straddr[128]; + + check_leaks_push(dyndns_test_ctx); + will_return_getifaddrs("eth0", "192.168.0.1", AF_INET); + will_return_getifaddrs("eth1", "192.168.0.2", AF_INET); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + ret = sss_iface_addr_list_get(dyndns_test_ctx, "eth0", &addrlist); + assert_int_equal(ret, EOK); + + /* There must be only one address with the correct value */ + assert_non_null(addrlist); + assert_non_null(addrlist->addr); + assert_null(addrlist->next); + assert_null(addrlist->prev); + + assert_non_null(inet_ntop(AF_INET, + &((struct sockaddr_in *) addrlist->addr)->sin_addr, + straddr, INET_ADDRSTRLEN)); + assert_string_equal(straddr, "192.168.0.1"); + + talloc_free(addrlist); + + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_get_multi_ifaddr(void **state) +{ + errno_t ret; + struct sss_iface_addr *addrlist; + struct sss_iface_addr *sss_if_addr; + char straddr[128]; + + check_leaks_push(dyndns_test_ctx); + will_return_getifaddrs("eth0", "192.168.0.2", AF_INET); + will_return_getifaddrs("eth0", "192.168.0.1", AF_INET); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + ret = sss_iface_addr_list_get(dyndns_test_ctx, "eth0", &addrlist); + assert_int_equal(ret, EOK); + + sss_if_addr = addrlist; + assert_non_null(sss_if_addr); + assert_non_null(sss_if_addr->addr); + assert_non_null(sss_if_addr->next); + assert_null(sss_if_addr->prev); + + assert_non_null(inet_ntop(AF_INET, + &((struct sockaddr_in *) sss_if_addr->addr)->sin_addr, + straddr, INET_ADDRSTRLEN)); + /* ip addresses are returned in different order */ + assert_string_equal(straddr, "192.168.0.1"); + + sss_if_addr = addrlist->next; + assert_non_null(sss_if_addr); + assert_non_null(sss_if_addr->addr); + assert_null(sss_if_addr->next); + assert_non_null(sss_if_addr->prev); + + assert_non_null(inet_ntop(AF_INET, + &((struct sockaddr_in *) sss_if_addr->addr)->sin_addr, + straddr, INET_ADDRSTRLEN)); + /* ip addresses are returned in different order */ + assert_string_equal(straddr, "192.168.0.2"); + + talloc_free(addrlist); + + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_get_ifaddr_enoent(void **state) +{ + errno_t ret; + struct sss_iface_addr *addrlist = NULL; + + check_leaks_push(dyndns_test_ctx); + will_return_getifaddrs("eth0", "192.168.0.1", AF_INET); + will_return_getifaddrs("eth1", "192.168.0.2", AF_INET); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + ret = sss_iface_addr_list_get(dyndns_test_ctx, "non_existing_interface", + &addrlist); + assert_int_equal(ret, ENOENT); + talloc_free(addrlist); + + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_addr_list_as_str_list(void **state) +{ + int i; + char **output; + errno_t ret; + struct sss_iface_addr *addrlist; + struct { + const char* addr; + int af; + } input[] = { + {"2001:cdba::555", AF_INET6}, + {"192.168.0.1", AF_INET}, + {"192.168.0.2", AF_INET}, + {"2001:cdba::444", AF_INET6} + }; + int size = 4; + + check_leaks_push(dyndns_test_ctx); + + for (i = 0; i < size; i++) { + will_return_getifaddrs("eth0", input[i].addr, input[i].af); + } + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + + ret = sss_iface_addr_list_get(dyndns_test_ctx, "eth0", &addrlist); + assert_int_equal(ret, EOK); + + ret = sss_iface_addr_list_as_str_list(dyndns_test_ctx, addrlist, &output); + assert_int_equal(ret, EOK); + for (i = 0; i < size; i++) { + /* addresses are returned in reversed order */ + assert_int_equal(strcmp(input[i].addr, output[size - 1 - i]), 0); + } + + talloc_free(addrlist); + talloc_free(output); + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_create_fwd_msg(void **state) +{ + errno_t ret; + char *msg; + struct sss_iface_addr *addrlist; + int i; + + check_leaks_push(dyndns_test_ctx); + + /* getifaddrs is called twice in sss_get_dualstack_addresses() */ + for (i = 0; i < 2; i++) { + will_return_getifaddrs("eth0", "192.168.0.2", AF_INET); + will_return_getifaddrs("eth1", "192.168.0.1", AF_INET); + will_return_getifaddrs("eth0", "2001:cdba::555", AF_INET6); + will_return_getifaddrs("eth1", "2001:cdba::444", AF_INET6); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + } + + struct sockaddr_in sin; + memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr ("192.168.0.2"); + ret = sss_get_dualstack_addresses(dyndns_test_ctx, + (struct sockaddr *) &sin, + &addrlist); + assert_int_equal(ret, EOK); + + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "\nupdate delete bran_stark. in A\n" + "update add bran_stark. 1234 in A 192.168.0.2\n" + "send\n" + "update delete bran_stark. in AAAA\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::555\n" + "send\n"); + talloc_zfree(msg); + + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, false, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "\nupdate delete bran_stark. in A\n" + "update add bran_stark. 1234 in A 192.168.0.2\n" + "update delete bran_stark. in AAAA\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::555\n" + "send\n"); + talloc_zfree(msg); + + /* fallback case realm and server */ + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, "North", "Winterfell", + "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "server Winterfell\n" + "realm North\n" + "update delete bran_stark. in A\n" + "update add bran_stark. 1234 in A 192.168.0.2\n" + "send\n" + "update delete bran_stark. in AAAA\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::555\n" + "send\n"); + talloc_zfree(msg); + + /* just realm */ + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, "North", NULL, + "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "realm North\n" + "update delete bran_stark. in A\n" + "update add bran_stark. 1234 in A 192.168.0.2\n" + "send\n" + "update delete bran_stark. in AAAA\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::555\n" + "send\n"); + talloc_zfree(msg); + + /* just server */ + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, "Winterfell", + "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "server Winterfell\n" + "\n" + "update delete bran_stark. in A\n" + "update add bran_stark. 1234 in A 192.168.0.2\n" + "send\n" + "update delete bran_stark. in AAAA\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::555\n" + "send\n"); + talloc_zfree(msg); + + /* remove just A */ + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark", + 1234, DYNDNS_REMOVE_A, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "\nupdate delete bran_stark. in A\n" + "update add bran_stark. 1234 in A 192.168.0.2\n" + "send\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::555\n" + "send\n"); + talloc_zfree(msg); + + /* remove just AAAA */ + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark", + 1234, DYNDNS_REMOVE_AAAA, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "\nupdate add bran_stark. 1234 in A 192.168.0.2\n" + "send\n" + "update delete bran_stark. in AAAA\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::555\n" + "send\n"); + talloc_zfree(msg); + + talloc_free(addrlist); + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_create_fwd_msg_mult(void **state) +{ + errno_t ret; + char *msg; + struct sss_iface_addr *addrlist; + int i; + + check_leaks_push(dyndns_test_ctx); + + /* getifaddrs is called twice in sss_get_dualstack_addresses() */ + for (i = 0; i < 2; i++) { + will_return_getifaddrs("eth0", "192.168.0.2", AF_INET); + will_return_getifaddrs("eth0", "192.168.0.1", AF_INET); + will_return_getifaddrs("eth0", "2001:cdba::555", AF_INET6); + will_return_getifaddrs("eth0", "2001:cdba::444", AF_INET6); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + } + + struct sockaddr_in sin; + memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr ("192.168.0.2"); + ret = sss_get_dualstack_addresses(dyndns_test_ctx, + (struct sockaddr *) &sin, + &addrlist); + assert_int_equal(ret, EOK); + + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "\nupdate delete bran_stark. in A\n" + "update add bran_stark. 1234 in A 192.168.0.1\n" + "update add bran_stark. 1234 in A 192.168.0.2\n" + "send\n" + "update delete bran_stark. in AAAA\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::444\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::555\n" + "send\n"); + talloc_zfree(msg); + + talloc_free(addrlist); + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_create_fwd_msg_A(void **state) +{ + errno_t ret; + char *msg; + struct sss_iface_addr *addrlist; + int i; + + check_leaks_push(dyndns_test_ctx); + + /* getifaddrs is called twice in sss_get_dualstack_addresses() */ + for (i = 0; i < 2; i++) { + will_return_getifaddrs("eth0", "192.168.0.2", AF_INET); + will_return_getifaddrs("eth0", "192.168.0.1", AF_INET); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + } + + struct sockaddr_in sin; + memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr ("192.168.0.2"); + ret = sss_get_dualstack_addresses(dyndns_test_ctx, + (struct sockaddr *) &sin, + &addrlist); + assert_int_equal(ret, EOK); + + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "\nupdate delete bran_stark. in A\n" + "update add bran_stark. 1234 in A 192.168.0.1\n" + "update add bran_stark. 1234 in A 192.168.0.2\n" + "send\n" + "update delete bran_stark. in AAAA\n" + "send\n"); + talloc_zfree(msg); + + talloc_free(addrlist); + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_create_fwd_msg_AAAA(void **state) +{ + errno_t ret; + char *msg; + struct sss_iface_addr *addrlist; + int i; + + check_leaks_push(dyndns_test_ctx); + + /* getifaddrs is called twice in sss_get_dualstack_addresses() */ + for (i = 0; i < 2; i++) { + will_return_getifaddrs("eth0", "2001:cdba::555", AF_INET6); + will_return_getifaddrs("eth0", "2001:cdba::444", AF_INET6); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + } + + struct sockaddr_in6 sin; + memset(&sin, 0, sizeof (sin)); + sin.sin6_family = AF_INET6; + ret = inet_pton(AF_INET6, "2001:cdba::555", &sin.sin6_addr.s6_addr); + assert_int_equal(ret, 1); + ret = sss_get_dualstack_addresses(dyndns_test_ctx, + (struct sockaddr *) &sin, + &addrlist); + assert_int_equal(ret, EOK); + + ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + + assert_string_equal(msg, + "\nupdate delete bran_stark. in A\n" + "send\n" + "update delete bran_stark. in AAAA\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::444\n" + "update add bran_stark. 1234 in AAAA 2001:cdba::555\n" + "send\n"); + talloc_zfree(msg); + + talloc_free(addrlist); + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_create_ptr_msg(void **state) +{ + errno_t ret; + char *msg; + struct sss_iface_addr *addrlist; + int i; + + check_leaks_push(dyndns_test_ctx); + + /* getifaddrs is called twice in sss_get_dualstack_addresses() */ + for (i = 0; i < 2; i++) { + will_return_getifaddrs("eth0", "192.168.0.2", AF_INET); + will_return_getifaddrs("eth0", "192.168.0.1", AF_INET); + will_return_getifaddrs("eth0", "2001:cdba::555", AF_INET6); + will_return_getifaddrs("eth0", "2001:cdba::444", AF_INET6); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + } + + struct sockaddr_in sin; + memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr ("192.168.0.2"); + ret = sss_get_dualstack_addresses(dyndns_test_ctx, + (struct sockaddr *) &sin, + &addrlist); + assert_int_equal(ret, EOK); + + ret = be_nsupdate_create_ptr_msg(dyndns_test_ctx, NULL, NULL, "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, true, &msg); + assert_int_equal(ret, EOK); + assert_string_equal(msg, + "\nupdate delete 1.0.168.192.in-addr.arpa. in PTR\n" + "update add 1.0.168.192.in-addr.arpa. 1234 in PTR bran_stark.\n" + "send\n" + "update delete 2.0.168.192.in-addr.arpa. in PTR\n" + "update add 2.0.168.192.in-addr.arpa. 1234 in PTR bran_stark.\n" + "send\n" + "update delete 4.4.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. in PTR\n" + "update add 4.4.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. 1234 in PTR bran_stark.\n" + "send\n" + "update delete 5.5.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. in PTR\n" + "update add 5.5.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. 1234 in PTR bran_stark.\n" + "send\n"); + talloc_zfree(msg); + + ret = be_nsupdate_create_ptr_msg(dyndns_test_ctx, NULL, NULL, "bran_stark", + 1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA, + addrlist, false, &msg); + assert_int_equal(ret, EOK); + assert_string_equal(msg, + "\nupdate delete 1.0.168.192.in-addr.arpa. in PTR\n" + "update add 1.0.168.192.in-addr.arpa. 1234 in PTR bran_stark.\n" + "send\n" + "update delete 2.0.168.192.in-addr.arpa. in PTR\n" + "update add 2.0.168.192.in-addr.arpa. 1234 in PTR bran_stark.\n" + "send\n" + "update delete 4.4.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. in PTR\n" + "update add 4.4.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. 1234 in PTR bran_stark.\n" + "send\n" + "update delete 5.5.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. in PTR\n" + "update add 5.5.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. 1234 in PTR bran_stark.\n" + "send\n"); + talloc_zfree(msg); + + talloc_free(addrlist); + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_dualstack(void **state) +{ + errno_t ret; + struct sss_iface_addr *addrlist; + struct sss_iface_addr *sss_if_addrs; + char straddr[128]; + int i; + + check_leaks_push(dyndns_test_ctx); + + /* getifaddrs is called twice in sss_get_dualstack_addresses() */ + for (i = 0; i < 2; i++) { + will_return_getifaddrs("eth0", "192.168.0.2", AF_INET); + will_return_getifaddrs("eth1", "192.168.0.1", AF_INET); + will_return_getifaddrs("eth0", "2001:cdba::555", AF_INET6); + will_return_getifaddrs("eth1", "2001:cdba::444", AF_INET6); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + } + + struct sockaddr_in sin; + memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr ("192.168.0.2"); + ret = sss_get_dualstack_addresses(dyndns_test_ctx, + (struct sockaddr *) &sin, + &addrlist); + assert_int_equal(ret, EOK); + + sss_if_addrs = addrlist; + assert_non_null(sss_if_addrs); + assert_non_null(sss_if_addrs->addr); + assert_non_null(sss_if_addrs->next); + assert_null(sss_if_addrs->prev); + + assert_non_null(inet_ntop(AF_INET6, + &((struct sockaddr_in6 *) sss_if_addrs->addr)->sin6_addr, + straddr, INET6_ADDRSTRLEN)); + /* ip addresses are returned in different order */ + assert_string_equal(straddr, "2001:cdba::555"); + + sss_if_addrs = addrlist->next; + assert_non_null(sss_if_addrs); + assert_non_null(sss_if_addrs->addr); + assert_null(sss_if_addrs->next); + assert_non_null(sss_if_addrs->prev); + + assert_non_null(inet_ntop(AF_INET, + &((struct sockaddr_in *) sss_if_addrs->addr)->sin_addr, + straddr, INET_ADDRSTRLEN)); + /* ip addresses are returned in different order */ + assert_string_equal(straddr, "192.168.0.2"); + + talloc_free(addrlist); + + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_dualstack_multiple_addresses(void **state) +{ + errno_t ret; + struct sss_iface_addr *addrlist; + struct sss_iface_addr *sss_if_addrs; + char straddr[128]; + int i; + + check_leaks_push(dyndns_test_ctx); + + /* getifaddrs is called twice in sss_get_dualstack_addresses() */ + for (i = 0; i < 2; i++) { + will_return_getifaddrs("eth0", "192.168.0.2", AF_INET); + will_return_getifaddrs("eth0", "192.168.0.1", AF_INET); + /* loopback - invalid for DNS (should be skipped) */ + will_return_getifaddrs("eth0", "::1", AF_INET6); + /* linklocal - invalid for DNS (should be skipped) */ + will_return_getifaddrs("eth0", "fe80::5054:ff:fe4a:65ae", AF_INET6); + will_return_getifaddrs("eth0", "2001:cdba::555", AF_INET6); + will_return_getifaddrs("eth0", "2001:cdba::444", AF_INET6); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + } + + struct sockaddr_in sin; + memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr ("192.168.0.2"); + ret = sss_get_dualstack_addresses(dyndns_test_ctx, + (struct sockaddr *) &sin, + &addrlist); + assert_int_equal(ret, EOK); + + sss_if_addrs = addrlist; + assert_non_null(sss_if_addrs); + assert_non_null(sss_if_addrs->addr); + assert_non_null(sss_if_addrs->next); + assert_null(sss_if_addrs->prev); + + assert_non_null(inet_ntop(AF_INET6, + &((struct sockaddr_in6 *) sss_if_addrs->addr)->sin6_addr, + straddr, INET6_ADDRSTRLEN)); + /* ip addresses are returned in different order */ + assert_string_equal(straddr, "2001:cdba::444"); + + sss_if_addrs = sss_if_addrs->next; + assert_non_null(sss_if_addrs); + assert_non_null(sss_if_addrs->addr); + assert_non_null(sss_if_addrs->prev); + assert_non_null(sss_if_addrs->next); + + assert_non_null(inet_ntop(AF_INET6, + &((struct sockaddr_in6 *) sss_if_addrs->addr)->sin6_addr, + straddr, INET6_ADDRSTRLEN)); + /* ip addresses are returned in different order */ + assert_string_equal(straddr, "2001:cdba::555"); + + sss_if_addrs = sss_if_addrs->next; + assert_non_null(sss_if_addrs); + assert_non_null(sss_if_addrs->addr); + assert_non_null(sss_if_addrs->next); + assert_non_null(sss_if_addrs->prev); + + assert_non_null(inet_ntop(AF_INET, + &((struct sockaddr_in *) sss_if_addrs->addr)->sin_addr, + straddr, INET_ADDRSTRLEN)); + /* ip addresses are returned in different order */ + assert_string_equal(straddr, "192.168.0.1"); + + sss_if_addrs = sss_if_addrs->next; + assert_non_null(sss_if_addrs); + assert_non_null(sss_if_addrs->addr); + assert_null(sss_if_addrs->next); + assert_non_null(sss_if_addrs->prev); + + assert_non_null(inet_ntop(AF_INET, + &((struct sockaddr_in *) sss_if_addrs->addr)->sin_addr, + straddr, INET_ADDRSTRLEN)); + /* ip addresses are returned in different order */ + assert_string_equal(straddr, "192.168.0.2"); + + talloc_free(addrlist); + + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_dualstack_no_iface(void **state) +{ + errno_t ret; + struct sss_iface_addr *addrlist; + + check_leaks_push(dyndns_test_ctx); + + will_return_getifaddrs("eth0", "192.168.0.2", AF_INET); + will_return_getifaddrs("eth1", "192.168.0.1", AF_INET); + will_return_getifaddrs("eth0", "2001:cdba::555", AF_INET6); + will_return_getifaddrs("eth1", "2001:cdba::444", AF_INET6); + will_return_getifaddrs(NULL, NULL, 0); /* sentinel */ + + struct sockaddr_in sin; + memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr ("192.168.0.3"); + ret = sss_get_dualstack_addresses(dyndns_test_ctx, + (struct sockaddr *) &sin, + &addrlist); + assert_int_equal(ret, ENOENT); + + assert_true(check_leaks_pop(dyndns_test_ctx) == true); +} + +void dyndns_test_ok(void **state) +{ + struct tevent_req *req; + errno_t ret; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(global_talloc_context); + assert_non_null(tmp_ctx); + check_leaks_push(tmp_ctx); + + dyndns_test_ctx->state = MOCK_NSUPDATE_OK; + + req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev, + BE_NSUPDATE_AUTH_GSS_TSIG, + discard_const("test message"), false); + assert_non_null(req); + tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(dyndns_test_ctx->tctx); + DEBUG(SSSDBG_TRACE_LIBS, + "Child request returned [%d]: %s\n", ret, strerror(ret)); + assert_int_equal(ret, EOK); + + assert_true(WIFEXITED(dyndns_test_ctx->child_status)); + assert_int_equal(WEXITSTATUS(dyndns_test_ctx->child_status), 0); + + assert_true(check_leaks_pop(tmp_ctx) == true); + talloc_free(tmp_ctx); +} + +void dyndns_test_error(void **state) +{ + struct tevent_req *req; + errno_t ret; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(global_talloc_context); + assert_non_null(tmp_ctx); + check_leaks_push(tmp_ctx); + + dyndns_test_ctx->state = MOCK_NSUPDATE_ERR; + + req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev, + BE_NSUPDATE_AUTH_GSS_TSIG, + discard_const("test message"), false); + assert_non_null(req); + tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx); + + /* Wait until the test finishes with EIO (child error) */ + ret = test_ev_loop(dyndns_test_ctx->tctx); + DEBUG(SSSDBG_TRACE_LIBS, + "Child request returned [%d]: %s\n", ret, strerror(ret)); + assert_int_equal(ret, ERR_DYNDNS_FAILED); + + assert_true(WIFEXITED(dyndns_test_ctx->child_status)); + assert_int_equal(WEXITSTATUS(dyndns_test_ctx->child_status), 1); + + assert_true(check_leaks_pop(tmp_ctx) == true); + talloc_free(tmp_ctx); +} + +void dyndns_test_timeout(void **state) +{ + struct tevent_req *req; + errno_t ret; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(global_talloc_context); + assert_non_null(tmp_ctx); + check_leaks_push(tmp_ctx); + + dyndns_test_ctx->state = MOCK_NSUPDATE_TIMEOUT; + + req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev, + BE_NSUPDATE_AUTH_GSS_TSIG, + discard_const("test message"), false); + assert_non_null(req); + tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx); + + /* Wait until the test finishes with EIO (child error) */ + ret = test_ev_loop(dyndns_test_ctx->tctx); + + /* The event queue may not be empty. We need to make sure that all events + * are processed. + */ + tevent_loop_wait(dyndns_test_ctx->tctx->ev); + + DEBUG(SSSDBG_TRACE_LIBS, + "Child request returned [%d]: %s\n", ret, strerror(ret)); + assert_int_equal(ret, ERR_DYNDNS_TIMEOUT); + + assert_true(check_leaks_pop(tmp_ctx) == true); + talloc_free(tmp_ctx); +} + +/* Testsuite setup and teardown */ +static int dyndns_test_setup(void **state) +{ + struct sss_test_conf_param params[] = { + { "dyndns_update", "true" }, + { "dyndns_refresh_interval", "2" }, + { NULL, NULL }, /* Sentinel */ + }; + + assert_true(leak_check_setup()); + global_mock_context = talloc_new(global_talloc_context); + assert_non_null(global_mock_context); + + dyndns_test_ctx = talloc_zero(global_talloc_context, struct dyndns_test_ctx); + assert_non_null(dyndns_test_ctx); + + dyndns_test_ctx->tctx = create_dom_test_ctx(dyndns_test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, params); + assert_non_null(dyndns_test_ctx->tctx); + + dyndns_test_ctx->be_ctx = mock_be_ctx(dyndns_test_ctx, dyndns_test_ctx->tctx); + assert_non_null(dyndns_test_ctx->be_ctx); + + return 0; +} + +static int dyndns_test_simple_setup(void **state) +{ + assert_true(leak_check_setup()); + global_mock_context = talloc_new(global_talloc_context); + assert_non_null(global_mock_context); + + dyndns_test_ctx = talloc_zero(global_talloc_context, struct dyndns_test_ctx); + assert_non_null(dyndns_test_ctx); + return 0; +} + +static int dyndns_test_teardown(void **state) +{ + talloc_free(dyndns_test_ctx); + talloc_free(global_mock_context); + assert_true(leak_check_teardown()); + return 0; +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + /* Utility functions unit test */ + cmocka_unit_test_setup_teardown(dyndns_test_sss_iface_addr_get_misc, + dyndns_test_simple_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_get_ifaddr, + dyndns_test_simple_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_get_multi_ifaddr, + dyndns_test_simple_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_get_ifaddr_enoent, + dyndns_test_simple_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_addr_list_as_str_list, + dyndns_test_simple_setup, + dyndns_test_teardown), + + /* Dynamic DNS update unit tests*/ + cmocka_unit_test_setup_teardown(dyndns_test_ok, + dyndns_test_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_error, + dyndns_test_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_timeout, + dyndns_test_setup, + dyndns_test_teardown), + + /* Dynamic DNS dualstack unit tests*/ + cmocka_unit_test_setup_teardown(dyndns_test_dualstack, + dyndns_test_simple_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_dualstack_multiple_addresses, + dyndns_test_simple_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_dualstack_no_iface, + dyndns_test_simple_setup, + dyndns_test_teardown), + + /* Messages for nsupdate */ + cmocka_unit_test_setup_teardown(dyndns_test_create_fwd_msg, + dyndns_test_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_create_fwd_msg_mult, + dyndns_test_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_create_fwd_msg_A, + dyndns_test_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_create_fwd_msg_AAAA, + dyndns_test_setup, + dyndns_test_teardown), + cmocka_unit_test_setup_teardown(dyndns_test_create_ptr_msg, + dyndns_test_setup, + dyndns_test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + return rv; +} diff --git a/src/tests/cmocka/test_expire_common.c b/src/tests/cmocka/test_expire_common.c new file mode 100644 index 0000000..98ebf7f --- /dev/null +++ b/src/tests/cmocka/test_expire_common.c @@ -0,0 +1,131 @@ +/* + Authors: + Pavel Reichl <preichl@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests - common code for password expiration tests + + 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 <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <setjmp.h> +#include <unistd.h> +#include <sys/types.h> +#include <cmocka.h> +#include <time.h> + +#include "tests/common.h" +#include "tests/cmocka/test_expire_common.h" + +#define MAX_VAL 100 + +static char *now_str(TALLOC_CTX *mem_ctx, const char* format, int s) +{ + time_t t = time(NULL) + s; + struct tm *tm; + size_t len; + char *timestr; + + timestr = talloc_array(mem_ctx, char, MAX_VAL); + + tm = gmtime(&t); + len = strftime(timestr, MAX_VAL, format, tm); + if (len == 0) { + return NULL; + } + + return timestr; +} + +int expire_test_setup(void **state) +{ + struct expire_test_ctx *exp_state; + TALLOC_CTX *mem_ctx; + char *past_time; + char *future_time; + char *invalid_format; + char *invalid_longer_format; + + mem_ctx = talloc_new(NULL); + assert_non_null(mem_ctx); + + exp_state = talloc(mem_ctx, struct expire_test_ctx); + assert_non_null(exp_state); + + *state = exp_state; + + /* testing data */ + invalid_format = now_str(exp_state, "%Y%m%d%H%M%S", -20); + assert_non_null(invalid_format); + + invalid_longer_format = (void*)now_str(exp_state, "%Y%m%d%H%M%SZA", -20); + assert_non_null(invalid_longer_format); + + past_time = (void*)now_str(exp_state, "%Y%m%d%H%M%SZ", -20); + assert_non_null(past_time); + + future_time = (void*)now_str(exp_state, "%Y%m%d%H%M%SZ", 20); + assert_non_null(future_time); + + exp_state->past_time = past_time; + exp_state->future_time = future_time; + exp_state->invalid_format = invalid_format; + exp_state->invalid_longer_format = invalid_longer_format; + + return 0; +} + +int expire_test_teardown(void **state) +{ + struct expire_test_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct expire_test_ctx); + assert_non_null(test_ctx); + + talloc_free(test_ctx); + + return 0; +} + +void expire_test_tz(const char* tz, + void (*test_func)(void*, void*), + void *test_in, + void *_test_out) +{ + errno_t ret; + const char *orig_tz = NULL; + + orig_tz = getenv("TZ"); + if (orig_tz == NULL) { + orig_tz = ""; + } + + if (tz) { + ret = setenv("TZ", tz, 1); + + assert_return_code(ret, errno); + } + + test_func(test_in, _test_out); + + /* restore */ + if (orig_tz != NULL) { + ret = setenv("TZ", orig_tz, 1); + assert_return_code(ret, errno); + } +} diff --git a/src/tests/cmocka/test_expire_common.h b/src/tests/cmocka/test_expire_common.h new file mode 100644 index 0000000..0ccff14 --- /dev/null +++ b/src/tests/cmocka/test_expire_common.h @@ -0,0 +1,39 @@ +/* + Authors: + Pavel Reichl <preichl@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests: Tests for password expiration related functionality + + 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 __TEST_EXPIRE_COMMON_H +#define __TEST_EXPIRE_COMMON_H + +struct expire_test_ctx +{ + char *past_time; + char *future_time; + char *invalid_format; + char *invalid_longer_format; +}; + +int expire_test_setup(void **state); +int expire_test_teardown(void **state); +void expire_test_tz(const char* tz, void (*f)(void*, void*), void *in, + void *_out); + +#endif /* __TEST_EXPIRE_COMMON_H */ diff --git a/src/tests/cmocka/test_find_uid.c b/src/tests/cmocka/test_find_uid.c new file mode 100644 index 0000000..63a426a --- /dev/null +++ b/src/tests/cmocka/test_find_uid.c @@ -0,0 +1,105 @@ +/* + SSSD + + find_uid - Utilities tests + + Authors: + Abhishek Singh <abhishekkumarsingh.cse@gmail.com> + + Copyright (C) 2013 Red Hat + + 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 <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <setjmp.h> +#include <unistd.h> +#include <sys/types.h> +#include <cmocka.h> +#include <dhash.h> + +#include "util/find_uid.h" +#include "tests/common.h" + +void test_check_if_uid_is_active_success(void **state) +{ + int ret; + uid_t uid; + bool result; + + uid = getuid(); + + ret = check_if_uid_is_active(uid, &result); + assert_true(ret == EOK); + assert_true(result); +} + +void test_check_if_uid_is_active_fail(void **state) +{ + int ret; + uid_t uid; + bool result; + + uid = (uid_t) -7; + + ret = check_if_uid_is_active(uid, &result); + assert_true(ret == EOK); + assert_true(!result); +} + +void test_get_uid_table(void **state) +{ + int ret; + uid_t uid; + TALLOC_CTX *tmp_ctx; + hash_table_t *table; + hash_key_t key; + hash_value_t value; + + tmp_ctx = talloc_new(NULL); + assert_true(tmp_ctx != NULL); + + ret = get_uid_table(tmp_ctx, &table); + assert_true(ret == EOK); + + uid = getuid(); + key.type = HASH_KEY_ULONG; + key.ul = (unsigned long) uid; + + ret = hash_lookup(table, &key, &value); + assert_true(ret == HASH_SUCCESS); + assert_true(hash_delete(table, &key) == HASH_SUCCESS); + + uid = (uid_t) -7; + key.type = HASH_KEY_ULONG; + key.ul = (unsigned long) uid; + + ret = hash_lookup(table, &key, &value); + assert_true(ret == HASH_ERROR_KEY_NOT_FOUND); + + talloc_free(tmp_ctx); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_check_if_uid_is_active_success), + cmocka_unit_test(test_check_if_uid_is_active_fail), + cmocka_unit_test(test_get_uid_table) + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_fo_srv.c b/src/tests/cmocka/test_fo_srv.c new file mode 100644 index 0000000..88eadca --- /dev/null +++ b/src/tests/cmocka/test_fo_srv.c @@ -0,0 +1,810 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: Resolver tests using a fake resolver library + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "providers/fail_over_srv.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" + +#define TEST_RESOLV_TIMEOUT 5 +#define TEST_FO_TIMEOUT 3000 +#define TEST_SRV_TTL 500 +#define TEST_SRV_SHORT_TTL 2 + +static TALLOC_CTX *global_mock_context = NULL; + +enum host_database default_host_dbs[] = { DB_FILES, DB_DNS, DB_SENTINEL }; + +struct resolv_ctx { + int foo; +}; + +/* mock resolver interface. The resolver test is separate */ +int resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, + int timeout, int ares_timeout, bool use_search_list, + struct resolv_ctx **ctxp) +{ + *ctxp = talloc(mem_ctx, struct resolv_ctx); + return EOK; +} + +struct tevent_req * +resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct resolv_ctx *ctx, const char *name, + enum restrict_family family_order, + enum host_database *db) +{ + return test_req_succeed_send(mem_ctx, ev); +} + +int resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + int *status, int *timeouts, + struct resolv_hostent **rhostent) +{ + return test_request_recv(req); +} + +const char *resolv_strerror(int ares_code) +{ + return NULL; +} + +struct tevent_req *resolv_discover_srv_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resolv_ctx *resolv_ctx, + const char *service, + const char *protocol, + const char **discovery_domains) +{ + return test_req_succeed_send(mem_ctx, ev); +} + +errno_t resolv_discover_srv_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct ares_srv_reply **_reply_list, + uint32_t *_ttl, + char **_dns_domain) +{ + struct ares_srv_reply *reply_list; + uint32_t ttl; + char *dns_domain; + + /* Need to always consume all mocked values */ + reply_list = sss_mock_ptr_type(struct ares_srv_reply *); + ttl = sss_mock_ptr_type(uint32_t); + dns_domain = sss_mock_ptr_type(char *); + + if (_reply_list != NULL) { + *_reply_list = reply_list; + } + + if (_ttl != NULL) { + *_ttl = ttl; + } + + if (_dns_domain != NULL) { + *_dns_domain = dns_domain; + } + + return test_request_recv(req); +} + +struct ares_srv_reply *pop_lowest_prio(struct ares_srv_reply **r) +{ + struct ares_srv_reply *lowest; + struct ares_srv_reply *iter; + struct ares_srv_reply *prev; + + lowest = *r; + iter = lowest; + while (iter != NULL) { + if (iter->priority < lowest->priority) { + lowest = iter; + } + + iter = iter->next; + } + + prev = NULL; + iter = *r; + while (iter != lowest) { + prev = iter; + iter = iter->next; + } + + /* iter points to the lowest prio. Prev points to the item before */ + if (prev) { + prev->next = lowest->next; + } else { + *r = lowest->next; + } + + return lowest; +} + +int resolv_sort_srv_reply(struct ares_srv_reply **reply) +{ + struct ares_srv_reply *r; + struct ares_srv_reply *lowest; + struct ares_srv_reply *sorted = NULL; + struct ares_srv_reply *sorted_head = NULL; + + r = *reply; + if (r == NULL || r->next == NULL) { + return EOK; + } + + do { + lowest = pop_lowest_prio(&r); + if (sorted) { + sorted->next = lowest; + sorted = sorted->next; + } else { + sorted = lowest; + sorted_head = sorted; + } + } while (r != NULL); + + *reply = sorted_head; + return EOK; +} + +struct tevent_req *resolv_get_domain_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resolv_ctx *resolv_ctx, + const char *hostname, + enum host_database *host_dbs, + enum restrict_family family_order) +{ + return test_req_succeed_send(mem_ctx, ev); +} + +errno_t resolv_get_domain_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + char **_dns_domain) +{ + return test_request_recv(req); +} + +/* The unit test */ +struct test_fo_ctx { + struct resolv_ctx *resolv; + struct fo_ctx *fo_ctx; + struct fo_resolve_srv_dns_ctx *srv_ctx; + struct fo_service *fo_svc; + struct sss_test_ctx *ctx; + int ttl; + + struct fo_server *srv; + + int num_done; +}; + +int test_fo_srv_data_cmp(void *ud1, void *ud2) +{ + return strcasecmp((char*) ud1, (char*) ud2); +} + +static int test_fo_setup(void **state) +{ + struct test_fo_ctx *test_ctx; + errno_t ret; + struct fo_options fopts; + + assert_true(leak_check_setup()); + global_mock_context = talloc_new(global_talloc_context); + assert_non_null(global_mock_context); + + test_ctx = talloc_zero(global_mock_context, + struct test_fo_ctx); + assert_non_null(test_ctx); + + test_ctx->ctx = create_ev_test_ctx(test_ctx); + assert_non_null(test_ctx->ctx); + + ret = resolv_init(test_ctx, test_ctx->ctx->ev, + TEST_RESOLV_TIMEOUT, 2000, true, &test_ctx->resolv); + assert_non_null(test_ctx->resolv); + + memset(&fopts, 0, sizeof(fopts)); + fopts.retry_timeout = TEST_FO_TIMEOUT; + fopts.family_order = IPV4_FIRST; + + test_ctx->fo_ctx = fo_context_init(test_ctx, &fopts); + assert_non_null(test_ctx->fo_ctx); + + ret = fo_new_service(test_ctx->fo_ctx, "ldap", + test_fo_srv_data_cmp, + &test_ctx->fo_svc); + assert_int_equal(ret, ERR_OK); + + *state = test_ctx; + return 0; +} + +static int test_fo_teardown(void **state) +{ + struct test_fo_ctx *test_ctx = + talloc_get_type(*state, struct test_fo_ctx); + + talloc_free(test_ctx); + talloc_free(global_mock_context); + assert_true(leak_check_teardown()); + return 0; +} + +static int test_fo_srv_setup(void **state) +{ + struct test_fo_ctx *test_ctx; + bool ok; + + test_fo_setup(state); + test_ctx = *state; + + test_ctx->srv_ctx = fo_resolve_srv_dns_ctx_init(test_ctx, test_ctx->resolv, + IPV4_FIRST, default_host_dbs, + "client.sssd.com", "sssd.local"); + assert_non_null(test_ctx->srv_ctx); + + ok = fo_set_srv_lookup_plugin(test_ctx->fo_ctx, + fo_resolve_srv_dns_send, + fo_resolve_srv_dns_recv, + test_ctx->srv_ctx); + assert_true(ok); + + *state = test_ctx; + return 0; +} + +static int test_fo_srv_teardown(void **state) +{ + test_fo_teardown(state); + return 0; +} + +/* reply_list and dns_domain must be a talloc context so it can be used as + * talloc_steal argument later + */ +static void mock_srv_results(struct ares_srv_reply *reply_list, + uint32_t ttl, + char *dns_domain) +{ + will_return(resolv_discover_srv_recv, reply_list); + will_return(resolv_discover_srv_recv, ttl); + will_return(resolv_discover_srv_recv, dns_domain); +} + +static void check_server(struct test_fo_ctx *ctx, + struct fo_server *srv, + int port, + const char *name) +{ + assert_non_null(srv); + assert_int_equal(fo_get_server_port(srv), port); + assert_string_equal(fo_get_server_name(srv), name); + + + if (ctx->srv_ctx) { + assert_true(fo_is_srv_lookup(srv)); + } +} + +static void test_fo_srv_step1(struct test_fo_ctx *test_ctx); +static void test_fo_srv_done1(struct tevent_req *req); +static void test_fo_srv_done2(struct tevent_req *req); +static void test_fo_srv_done3(struct tevent_req *req); +static void test_fo_srv_done4(struct tevent_req *req); +static void test_fo_srv_done5(struct tevent_req *req); + + +struct ares_srv_reply * +mock_ares_reply(TALLOC_CTX *mem_ctx, const char *hostname, + int weight, int priority, int port) +{ + struct ares_srv_reply *s; + + s = talloc_zero(mem_ctx, struct ares_srv_reply); + if (s == NULL) { + return NULL; + } + + s->host = talloc_strdup(s, hostname); + if (s->host == NULL) { + talloc_free(s); + return NULL; + } + + s->weight = weight; + s->priority = priority; + s->port = port; + + return s; +} + +static void test_fo_srv_mock_dns(struct test_fo_ctx *test_ctx, + int ttl) +{ + struct ares_srv_reply *s1; + struct ares_srv_reply *s2; + char *dns_domain; + + s1 = mock_ares_reply(test_ctx, "ldap1.sssd.com", 100, 1, 389); + assert_non_null(s1); + + s2 = mock_ares_reply(test_ctx, "ldap2.sssd.com", 100, 2, 389); + assert_non_null(s2); + + s1->next = s2; + + dns_domain = talloc_strdup(test_ctx, "sssd.com"); + assert_non_null(dns_domain); + + mock_srv_results(s1, ttl, dns_domain); +} + +static void test_fo_srv(void **state) +{ + errno_t ret; + struct test_fo_ctx *test_ctx = + talloc_get_type(*state, struct test_fo_ctx); + + test_fo_srv_mock_dns(test_ctx, TEST_SRV_TTL); + + ret = fo_add_srv_server(test_ctx->fo_svc, "_ldap", "sssd.com", + "sssd.local", "tcp", test_ctx); + assert_int_equal(ret, ERR_OK); + + test_fo_srv_step1(test_ctx); + + ret = test_ev_loop(test_ctx->ctx); + assert_int_equal(ret, ERR_OK); +} + +static void test_fo_srv_step1(struct test_fo_ctx *test_ctx) +{ + struct tevent_req *req; + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_done1, test_ctx); +} + +static void test_fo_srv_done1(struct tevent_req *req) +{ + struct test_fo_ctx *test_ctx = \ + tevent_req_callback_data(req, struct test_fo_ctx); + struct fo_server *srv; + errno_t ret; + + ret = fo_resolve_service_recv(req, req, &srv); + talloc_zfree(req); + assert_int_equal(ret, ERR_OK); + + /* ldap1.sssd.com has lower priority, it must always be first */ + check_server(test_ctx, srv, 389, "ldap1.sssd.com"); + + /* Mark the server as working and request the service again. The same server + * must be returned */ + fo_set_server_status(srv, SERVER_WORKING); + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_done2, test_ctx); +} + +static void test_fo_srv_done2(struct tevent_req *req) +{ + struct test_fo_ctx *test_ctx = \ + tevent_req_callback_data(req, struct test_fo_ctx); + struct fo_server *srv; + errno_t ret; + + ret = fo_resolve_service_recv(req, req, &srv); + talloc_zfree(req); + assert_int_equal(ret, ERR_OK); + + /* Must be ldap1 again */ + check_server(test_ctx, srv, 389, "ldap1.sssd.com"); + + /* Mark it at wrong, next lookup should yield ldap2 */ + fo_set_server_status(srv, SERVER_NOT_WORKING); + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_done3, test_ctx); +} + +static void test_fo_srv_done3(struct tevent_req *req) +{ + struct test_fo_ctx *test_ctx = \ + tevent_req_callback_data(req, struct test_fo_ctx); + struct fo_server *srv; + errno_t ret; + + ret = fo_resolve_service_recv(req, req, &srv); + talloc_zfree(req); + assert_int_equal(ret, ERR_OK); + + /* Must be ldap2 now */ + check_server(test_ctx, srv, 389, "ldap2.sssd.com"); + + /* Mark is at wrong, next lookup must reach the end of the server list */ + fo_set_server_status(srv, SERVER_NOT_WORKING); + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_done4, test_ctx); +} + +static void test_fo_srv_done4(struct tevent_req *req) +{ + struct test_fo_ctx *test_ctx = \ + tevent_req_callback_data(req, struct test_fo_ctx); + struct fo_server *srv; + errno_t ret; + + ret = fo_resolve_service_recv(req, req, &srv); + talloc_zfree(req); + /* No servers are left..*/ + assert_int_equal(ret, ENOENT); + + /* reset the server status and try again.. */ + fo_reset_servers(test_ctx->fo_svc); + if (test_ctx->srv_ctx) { + test_fo_srv_mock_dns(test_ctx, TEST_SRV_TTL); + } + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_done5, test_ctx); +} + +static void test_fo_srv_done5(struct tevent_req *req) +{ + struct test_fo_ctx *test_ctx = \ + tevent_req_callback_data(req, struct test_fo_ctx); + struct fo_server *srv; + errno_t ret; + + ret = fo_resolve_service_recv(req, req, &srv); + talloc_zfree(req); + + assert_int_equal(ret, ERR_OK); + + /* ldap1.sssd.com has lower priority, it must always be first */ + check_server(test_ctx, srv, 389, "ldap1.sssd.com"); + + /* OK, we made a full circle with the test, done */ + test_ctx->ctx->error = ERR_OK; + test_ctx->ctx->done = true; +} + +/* Make sure that two queries more than TTL seconds apart resolve + * into two different lists + */ +static void test_fo_srv_ttl_change_step(struct test_fo_ctx *test_ctx); +static void test_fo_srv_before(struct tevent_req *req); +static void test_fo_srv_after(struct tevent_req *req); + +void test_fo_srv_ttl_change(void **state) +{ + struct test_fo_ctx *test_ctx = + talloc_get_type(*state, struct test_fo_ctx); + + test_ctx->ttl = TEST_SRV_SHORT_TTL; + test_fo_srv_ttl_change_step(test_ctx); +} + +static void test_fo_srv_ttl_change_step(struct test_fo_ctx *test_ctx) +{ + errno_t ret; + struct tevent_req *req; + + test_fo_srv_mock_dns(test_ctx, test_ctx->ttl); + + ret = fo_add_srv_server(test_ctx->fo_svc, "_ldap", "sssd.com", + "sssd.local", "tcp", test_ctx); + assert_int_equal(ret, ERR_OK); + + ret = fo_add_server(test_ctx->fo_svc, "ldap1.sssd.com", + 389, (void *) discard_const("ldap://ldap1.sssd.com"), + true); + assert_int_equal(ret, ERR_OK); + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_before, test_ctx); + + ret = test_ev_loop(test_ctx->ctx); + assert_int_equal(ret, ERR_OK); +} + +static void test_fo_srv_before(struct tevent_req *req) +{ + struct test_fo_ctx *test_ctx = \ + tevent_req_callback_data(req, struct test_fo_ctx); + struct ares_srv_reply *s1; + struct ares_srv_reply *s2; + char *dns_domain; + errno_t ret; + + ret = fo_resolve_service_recv(req, test_ctx, &test_ctx->srv); + talloc_zfree(req); + assert_int_equal(ret, ERR_OK); + + DEBUG(SSSDBG_TRACE_FUNC, "Before TTL change\n"); + + check_server(test_ctx, test_ctx->srv, 389, "ldap1.sssd.com"); + fo_set_server_status(test_ctx->srv, SERVER_WORKING); + + /* Simulate changing the DNS environment. Change the host names */ + s1 = mock_ares_reply(test_ctx, "ldap1.sssd.com", 100, 2, 389); + assert_non_null(s1); + + s2 = mock_ares_reply(test_ctx, "ldap2.sssd.com", 100, 1, 389); + assert_non_null(s2); + + s1->next = s2; + + dns_domain = talloc_strdup(test_ctx, "sssd.com"); + assert_non_null(dns_domain); + + mock_srv_results(s1, test_ctx->ttl, dns_domain); + sleep(test_ctx->ttl + 1); + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_after, test_ctx); +} + +static void test_fo_srv_after2(struct tevent_req *req); + +static void test_fo_srv_after(struct tevent_req *req) +{ + struct test_fo_ctx *test_ctx = \ + tevent_req_callback_data(req, struct test_fo_ctx); + struct fo_server *srv; + errno_t ret; + struct ares_srv_reply *s1; + struct ares_srv_reply *s2; + char *dns_domain; + + ret = fo_resolve_service_recv(req, req, &srv); + talloc_zfree(req); + assert_int_equal(ret, ERR_OK); + + /* Try accessing server from a previous iteration. The + * server should be collapsed, but at least we shouldn't crash + */ + fo_set_server_status(test_ctx->srv, SERVER_WORKING); + + sleep(test_ctx->ttl + 1); + + /* Must be a different server now */ + check_server(test_ctx, srv, 389, "ldap2.sssd.com"); + + /* Simulate changing the DNS environment. Change the host names */ + s1 = mock_ares_reply(test_ctx, "ldap1.sssd.com", 100, 1, 389); + assert_non_null(s1); + + s2 = mock_ares_reply(test_ctx, "ldap2.sssd.com", 100, 2, 389); + assert_non_null(s2); + + s1->next = s2; + + dns_domain = talloc_strdup(test_ctx, "sssd.com"); + assert_non_null(dns_domain); + + mock_srv_results(s1, test_ctx->ttl, dns_domain); + sleep(test_ctx->ttl + 1); + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_after2, test_ctx); +} + +static void test_fo_srv_after2(struct tevent_req *req) +{ + struct test_fo_ctx *test_ctx = \ + tevent_req_callback_data(req, struct test_fo_ctx); + struct fo_server *srv; + errno_t ret; + + ret = fo_resolve_service_recv(req, req, &srv); + talloc_zfree(req); + assert_int_equal(ret, ERR_OK); + + /* Must be a different server now */ + check_server(test_ctx, srv, 389, "ldap1.sssd.com"); + + test_ctx->ctx->error = ERR_OK; + test_ctx->ctx->done = true; +} + +void test_fo_srv_ttl_zero(void **state) +{ + struct test_fo_ctx *test_ctx = + talloc_get_type(*state, struct test_fo_ctx); + + test_ctx->ttl = 0; + test_fo_srv_ttl_change_step(test_ctx); +} + +static void test_fo_hostlist(void **state) +{ + errno_t ret; + struct test_fo_ctx *test_ctx = + talloc_get_type(*state, struct test_fo_ctx); + + ret = fo_add_server(test_ctx->fo_svc, + "ldap1.sssd.com", 389, test_ctx, true); + assert_int_equal(ret, ERR_OK); + + ret = fo_add_server(test_ctx->fo_svc, + "ldap2.sssd.com", 389, test_ctx, true); + assert_int_equal(ret, ERR_OK); + + test_fo_srv_step1(test_ctx); + + ret = test_ev_loop(test_ctx->ctx); + assert_int_equal(ret, ERR_OK); +} + +static void test_fo_srv_dup_done(struct tevent_req *req); + +/* Test that running two parallel SRV queries doesn't return an error. + * This is a regression test for https://fedorahosted.org/sssd/ticket/3131 + */ +void test_fo_srv_duplicates(void **state) +{ + errno_t ret; + struct tevent_req *req; + struct test_fo_ctx *test_ctx = + talloc_get_type(*state, struct test_fo_ctx); + + test_fo_srv_mock_dns(test_ctx, test_ctx->ttl); + test_fo_srv_mock_dns(test_ctx, test_ctx->ttl); + + ret = fo_add_srv_server(test_ctx->fo_svc, "_ldap", "sssd.com", + "sssd.local", "tcp", test_ctx); + assert_int_equal(ret, ERR_OK); + + ret = fo_add_server(test_ctx->fo_svc, "ldap1.sssd.com", + 389, (void *) discard_const("ldap://ldap1.sssd.com"), + true); + assert_int_equal(ret, ERR_OK); + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_dup_done, test_ctx); + + req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, test_ctx->fo_ctx, + test_ctx->fo_svc); + assert_non_null(req); + tevent_req_set_callback(req, test_fo_srv_dup_done, test_ctx); + + ret = test_ev_loop(test_ctx->ctx); + assert_int_equal(ret, ERR_OK); +} + +static void test_fo_srv_dup_done(struct tevent_req *req) +{ + struct test_fo_ctx *test_ctx = \ + tevent_req_callback_data(req, struct test_fo_ctx); + errno_t ret; + const char *name; + + ret = fo_resolve_service_recv(req, test_ctx, &test_ctx->srv); + talloc_zfree(req); + assert_int_equal(ret, EOK); + + name = fo_get_server_name(test_ctx->srv); + assert_string_equal(name, "ldap1.sssd.com"); + + test_ctx->num_done++; + if (test_ctx->num_done == 2) { + test_ctx->ctx->error = ERR_OK; + test_ctx->ctx->done = true; + } +} + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_fo_hostlist, + test_fo_setup, + test_fo_teardown), + cmocka_unit_test_setup_teardown(test_fo_srv, + test_fo_srv_setup, + test_fo_srv_teardown), + cmocka_unit_test_setup_teardown(test_fo_srv_ttl_change, + test_fo_srv_setup, + test_fo_srv_teardown), + cmocka_unit_test_setup_teardown(test_fo_srv_ttl_zero, + test_fo_srv_setup, + test_fo_srv_teardown), + cmocka_unit_test_setup_teardown(test_fo_srv_duplicates, + test_fo_srv_setup, + test_fo_srv_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + return rv; +} diff --git a/src/tests/cmocka/test_fqnames.c b/src/tests/cmocka/test_fqnames.c new file mode 100644 index 0000000..efb3f57 --- /dev/null +++ b/src/tests/cmocka/test_fqnames.c @@ -0,0 +1,607 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: Fully Qualified Names Tests + + 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 <popt.h> + +#include "db/sysdb_private.h" +#include "tests/cmocka/common_mock.h" + +#define NAME "name" +#define DOMNAME "domname" +#define FLATNAME "flatname" +#define SPECIALNAME "[]{}();:'|\",<.>/?!#$%^&*_+~`" +#define PROVIDER "proxy" +#define CONNNAME "conn" + +#define DOMNAME2 "domname2" +#define FLATNAME2 "flatname2" + +#define SUBDOMNAME "subdomname" +#define SUBFLATNAME "subflatname" + +static struct sss_domain_info *create_test_domain(TALLOC_CTX *mem_ctx, + const char *name, + const char *flatname, + struct sss_domain_info *parent, + struct sss_names_ctx *nctx) +{ + struct sss_domain_info *dom; + + dom = talloc_zero(mem_ctx, struct sss_domain_info); + assert_non_null(dom); + + /* just to make new_subdomain happy */ + dom->sysdb = talloc_zero(dom, struct sysdb_ctx); + assert_non_null(dom->sysdb); + + dom->name = discard_const(name); + dom->flat_name = discard_const(flatname); + dom->parent = parent; + dom->names = nctx; + dom->provider = discard_const(PROVIDER); + dom->conn_name = discard_const(CONNNAME); + + return dom; +} + +struct fqdn_test_ctx { + struct sss_domain_info *dom; + + struct sss_names_ctx *nctx; +}; + +static int fqdn_test_setup(void **state) +{ + struct fqdn_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct fqdn_test_ctx); + assert_non_null(test_ctx); + + test_ctx->dom = create_test_domain(test_ctx, DOMNAME, FLATNAME, + NULL, NULL); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int fqdn_test_teardown(void **state) +{ + struct fqdn_test_ctx *test_ctx = talloc_get_type(*state, + struct fqdn_test_ctx); + + if (test_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return 1; + } + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void test_default(void **state) +{ + struct fqdn_test_ctx *test_ctx = talloc_get_type(*state, + struct fqdn_test_ctx); + errno_t ret; + + char *fqdn; + const int fqdn_size = 255; + char fqdn_s[fqdn_size]; + + if (test_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return; + } + + ret = sss_names_init_from_args(test_ctx, + SSS_DEFAULT_RE, + "%1$s@%2$s", &test_ctx->nctx); + assert_int_equal(ret, EOK); + + fqdn = sss_tc_fqname(test_ctx, test_ctx->nctx, test_ctx->dom, NAME); + assert_non_null(fqdn); + assert_string_equal(fqdn, NAME"@"DOMNAME); + talloc_free(fqdn); + + ret = sss_fqname(fqdn_s, fqdn_size, test_ctx->nctx, test_ctx->dom, NAME); + assert_int_equal(ret + 1, sizeof(NAME"@"DOMNAME)); + assert_string_equal(fqdn_s, NAME"@"DOMNAME); + + talloc_free(test_ctx->nctx); +} + +void test_all(void **state) +{ + struct fqdn_test_ctx *test_ctx = talloc_get_type(*state, + struct fqdn_test_ctx); + errno_t ret; + + char *fqdn; + const int fqdn_size = 255; + char fqdn_s[fqdn_size]; + + if (test_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return; + } + + ret = sss_names_init_from_args(test_ctx, + SSS_DEFAULT_RE, + "%1$s@%2$s@%3$s", &test_ctx->nctx); + assert_int_equal(ret, EOK); + + fqdn = sss_tc_fqname(test_ctx, test_ctx->nctx, test_ctx->dom, NAME); + assert_non_null(fqdn); + assert_string_equal(fqdn, NAME"@"DOMNAME"@"FLATNAME); + talloc_free(fqdn); + + ret = sss_fqname(fqdn_s, fqdn_size, test_ctx->nctx, test_ctx->dom, NAME); + assert_int_equal(ret + 1, sizeof(NAME"@"DOMNAME"@"FLATNAME)); + assert_string_equal(fqdn_s, NAME"@"DOMNAME"@"FLATNAME); + + talloc_free(test_ctx->nctx); +} + +void test_flat(void **state) +{ + struct fqdn_test_ctx *test_ctx = talloc_get_type(*state, + struct fqdn_test_ctx); + errno_t ret; + + char *fqdn; + const int fqdn_size = 255; + char fqdn_s[fqdn_size]; + + if (test_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return; + } + + ret = sss_names_init_from_args(test_ctx, + SSS_DEFAULT_RE, + "%1$s@%3$s", &test_ctx->nctx); + assert_int_equal(ret, EOK); + + fqdn = sss_tc_fqname(test_ctx, test_ctx->nctx, test_ctx->dom, NAME); + assert_non_null(fqdn); + assert_string_equal(fqdn, NAME"@"FLATNAME); + talloc_free(fqdn); + + ret = sss_fqname(fqdn_s, fqdn_size, test_ctx->nctx, test_ctx->dom, NAME); + assert_int_equal(ret + 1, sizeof(NAME"@"FLATNAME)); + assert_string_equal(fqdn_s, NAME"@"FLATNAME); + + talloc_free(test_ctx->nctx); +} + +void test_flat_fallback(void **state) +{ + struct fqdn_test_ctx *test_ctx = talloc_get_type(*state, + struct fqdn_test_ctx); + errno_t ret; + + char *fqdn; + const int fqdn_size = 255; + char fqdn_s[fqdn_size]; + + if (test_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return; + } + + ret = sss_names_init_from_args(test_ctx, + SSS_DEFAULT_RE, + "%1$s@%3$s", &test_ctx->nctx); + assert_int_equal(ret, EOK); + + test_ctx->dom->flat_name = NULL; + + /* If flat name is requested but does not exist, the code falls back to domain + * name + */ + fqdn = sss_tc_fqname(test_ctx, test_ctx->nctx, test_ctx->dom, NAME); + assert_non_null(fqdn); + assert_string_equal(fqdn, NAME"@"DOMNAME); + talloc_free(fqdn); + + ret = sss_fqname(fqdn_s, fqdn_size, test_ctx->nctx, test_ctx->dom, NAME); + assert_int_equal(ret + 1, sizeof(NAME"@"DOMNAME)); + assert_string_equal(fqdn_s, NAME"@"DOMNAME); + + talloc_free(test_ctx->nctx); +} + +struct parse_name_test_ctx { + struct sss_domain_info *dom; + struct sss_domain_info *subdom; + struct sss_names_ctx *nctx; +}; + +void parse_name_check(struct parse_name_test_ctx *test_ctx, + const char *full_name, + const char *default_domain, + const char exp_ret, + const char *exp_name, + const char *exp_domain) +{ + errno_t ret; + char *domain = NULL; + char *name = NULL; + + check_leaks_push(test_ctx); + ret = sss_parse_name_for_domains(test_ctx, test_ctx->dom, default_domain, + full_name, &domain, &name); + assert_int_equal(ret, exp_ret); + + if (exp_name) { + assert_non_null(name); + assert_string_equal(name, exp_name); + } + + if (exp_domain) { + assert_non_null(domain); + assert_string_equal(domain, exp_domain); + } + + talloc_free(name); + talloc_free(domain); + assert_true(check_leaks_pop(test_ctx) == true); +} + +static int parse_name_test_setup_re(void **state, const char *regexp) +{ + struct parse_name_test_ctx *test_ctx; + struct sss_domain_info *dom; + errno_t ret; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct parse_name_test_ctx); + assert_non_null(test_ctx); + + /* Init with an AD-style regex to be able to test flat name */ + ret = sss_names_init_from_args(test_ctx, + regexp, + "%1$s@%2$s", &test_ctx->nctx); + assert_int_equal(ret, EOK); + + /* The setup is two domains, first one with no subdomains, + * second one with a single subdomain + */ + dom = create_test_domain(test_ctx, DOMNAME, FLATNAME, + NULL, test_ctx->nctx); + assert_non_null(dom); + DLIST_ADD_END(test_ctx->dom, dom, struct sss_domain_info *); + + dom = create_test_domain(test_ctx, DOMNAME2, + FLATNAME2, NULL, test_ctx->nctx); + assert_non_null(dom); + DLIST_ADD_END(test_ctx->dom, dom, struct sss_domain_info *); + + /* Create the subdomain, but don't add it yet, we want to be able to + * test sss_parse_name_for_domains() signaling that domains must be + * discovered + */ + test_ctx->subdom = new_subdomain(dom, dom, SUBDOMNAME, NULL, SUBFLATNAME, + SUBDOMNAME, NULL, MPG_DISABLED, false, + NULL, NULL, 0, NULL, true); + assert_non_null(test_ctx->subdom); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int parse_name_test_setup_ipa_ad(void **state) +{ + return parse_name_test_setup_re(state, SSS_IPA_AD_DEFAULT_RE); +} + +static int parse_name_test_setup_default(void **state) +{ + return parse_name_test_setup_re(state, SSS_DEFAULT_RE); +} + +static int parse_name_test_two_names_ctx_setup(void **state) +{ + struct parse_name_test_ctx *test_ctx; + struct sss_names_ctx *nctx1 = NULL; + struct sss_names_ctx *nctx2 = NULL; + struct sss_domain_info *dom; + int ret; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct parse_name_test_ctx); + assert_non_null(test_ctx); + + ret = sss_names_init_from_args(test_ctx, SSS_DEFAULT_RE, + "%1$s@%2$s", &nctx1); + assert_int_equal(ret, EOK); + + ret = sss_names_init_from_args(test_ctx, SSS_IPA_AD_DEFAULT_RE, + "%1$s@%2$s", &nctx2); + assert_int_equal(ret, EOK); + + test_ctx->dom = create_test_domain(test_ctx, DOMNAME, FLATNAME, + NULL, nctx1); + assert_non_null(test_ctx->dom); + + dom = create_test_domain(test_ctx, DOMNAME2, FLATNAME2, + NULL, nctx2); + assert_non_null(dom); + DLIST_ADD_END(test_ctx->dom, dom, struct sss_domain_info *); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int parse_name_test_teardown(void **state) +{ + struct parse_name_test_ctx *test_ctx = talloc_get_type(*state, + struct parse_name_test_ctx); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void sss_parse_name_check(struct parse_name_test_ctx *test_ctx, + const char *input_name, + const int exp_ret, + const char *exp_name, + const char *exp_domain) +{ + errno_t ret; + char *domain = NULL; + char *name = NULL; + + check_leaks_push(test_ctx); + ret = sss_parse_name(test_ctx, test_ctx->nctx, input_name, + &domain, &name); + assert_int_equal(ret, exp_ret); + + if (exp_name) { + assert_non_null(name); + assert_string_equal(name, exp_name); + } + + if (exp_domain) { + assert_non_null(domain); + assert_string_equal(domain, exp_domain); + } + + talloc_zfree(name); + talloc_zfree(domain); + + assert_true(check_leaks_pop(test_ctx) == true); +} + +void parse_name_plain(void **state) +{ + struct parse_name_test_ctx *test_ctx = talloc_get_type(*state, + struct parse_name_test_ctx); + int ret; + + parse_name_check(test_ctx, NAME, NULL, EOK, NAME, NULL); + + ret = sss_parse_name(test_ctx, test_ctx->nctx, NAME, + NULL, NULL); + assert_int_equal(ret, EOK); + + sss_parse_name_check(test_ctx, NAME, EOK, NAME, NULL); + sss_parse_name_check(test_ctx, SPECIALNAME, EOK, SPECIALNAME, NULL); +} + +void parse_name_fqdn(void **state) +{ + struct parse_name_test_ctx *test_ctx = talloc_get_type(*state, + struct parse_name_test_ctx); + parse_name_check(test_ctx, NAME"@"DOMNAME, NULL, EOK, NAME, DOMNAME); + parse_name_check(test_ctx, NAME"@"DOMNAME2, NULL, EOK, NAME, DOMNAME2); + + sss_parse_name_check(test_ctx, NAME"@"DOMNAME, EOK, NAME, DOMNAME); + sss_parse_name_check(test_ctx, NAME"@"DOMNAME2, EOK, NAME, DOMNAME2); + sss_parse_name_check(test_ctx, "@"NAME"@"DOMNAME, EOK, "@"NAME, DOMNAME); + sss_parse_name_check(test_ctx, "@"NAME"@"DOMNAME2, EOK, "@"NAME, DOMNAME2); + sss_parse_name_check(test_ctx, DOMNAME"\\"NAME, EOK, NAME, DOMNAME); + sss_parse_name_check(test_ctx, DOMNAME2"\\"NAME, EOK, NAME, DOMNAME2); +} + +void parse_name_sub(void **state) +{ + struct parse_name_test_ctx *test_ctx = talloc_get_type(*state, + struct parse_name_test_ctx); + /* The subdomain name is valid, but not known */ + parse_name_check(test_ctx, NAME"@"SUBDOMNAME, NULL, EAGAIN, NULL, NULL); + + /* Link the subdomain (simulating subdom handler) and retry */ + test_ctx->dom->subdomains = test_ctx->subdom; + parse_name_check(test_ctx, NAME"@"SUBDOMNAME, NULL, EOK, NAME, SUBDOMNAME); +} + +void parse_name_flat(void **state) +{ + struct parse_name_test_ctx *test_ctx = talloc_get_type(*state, + struct parse_name_test_ctx); + + /* Link the subdomain (simulating subdom handler) */ + parse_name_check(test_ctx, FLATNAME"\\"NAME, NULL, EOK, NAME, DOMNAME); + parse_name_check(test_ctx, FLATNAME2"\\"NAME, NULL, EOK, NAME, DOMNAME2); + + /* The subdomain name is valid, but not known */ + parse_name_check(test_ctx, SUBFLATNAME"\\"NAME, NULL, EAGAIN, NULL, NULL); + test_ctx->dom->subdomains = test_ctx->subdom; + parse_name_check(test_ctx, SUBFLATNAME"\\"NAME, NULL, EOK, NAME, SUBDOMNAME); +} + +void parse_name_default(void **state) +{ + struct parse_name_test_ctx *test_ctx = talloc_get_type(*state, + struct parse_name_test_ctx); + struct sss_domain_info *dom2; + + parse_name_check(test_ctx, NAME, DOMNAME2, EOK, NAME, DOMNAME2); + dom2 = test_ctx->dom->next; + + /* Simulate unknown default domain */ + DLIST_REMOVE(test_ctx->dom, dom2); + parse_name_check(test_ctx, NAME, DOMNAME2, EAGAIN, NULL, NULL); +} + +void test_init_nouser(void **state) +{ + struct fqdn_test_ctx *test_ctx = talloc_get_type(*state, + struct fqdn_test_ctx); + errno_t ret; + + if (test_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return; + } + + ret = sss_names_init_from_args(test_ctx, + SSS_DEFAULT_RE, + "%2$s@%3$s", &test_ctx->nctx); + /* Initialization with no user name must fail */ + assert_int_not_equal(ret, EOK); +} + +void test_different_regexps(void **state) +{ + struct parse_name_test_ctx *test_ctx = talloc_get_type(*state, + struct parse_name_test_ctx); + parse_name_check(test_ctx, NAME"@"DOMNAME, NULL, EOK, NAME, DOMNAME); + parse_name_check(test_ctx, NAME"@"DOMNAME2, NULL, EOK, NAME, DOMNAME2); + parse_name_check(test_ctx, NAME"@WITH_AT@"DOMNAME2, NULL, EOK, NAME"@WITH_AT", DOMNAME2); + parse_name_check(test_ctx, "@LEADING_AT"NAME"@"DOMNAME, NULL, EOK, "@LEADING_AT"NAME, DOMNAME); + parse_name_check(test_ctx, "@LEADING_AT"NAME"@"DOMNAME2, NULL, EOK, "@LEADING_AT"NAME, DOMNAME2); + parse_name_check(test_ctx, "@LEADING_AT"NAME"@WITH_AT@"DOMNAME, NULL, EOK, "@LEADING_AT"NAME"@WITH_AT", DOMNAME); + parse_name_check(test_ctx, "@LEADING_AT"NAME"@WITH_AT@"DOMNAME2, NULL, EOK, "@LEADING_AT"NAME"@WITH_AT", DOMNAME2); + parse_name_check(test_ctx, FLATNAME"\\"NAME, NULL, EOK, FLATNAME"\\"NAME, NULL); + parse_name_check(test_ctx, FLATNAME2"\\"NAME, NULL, EOK, NAME, DOMNAME2); + parse_name_check(test_ctx, FLATNAME2"\\"NAME"@WITH_AT", NULL, EOK, NAME"@WITH_AT", DOMNAME2); +} + +void sss_parse_name_fail_ipa_ad(void **state) +{ + struct parse_name_test_ctx *test_ctx = talloc_get_type(*state, + struct parse_name_test_ctx); + + sss_parse_name_check(test_ctx, "", ERR_REGEX_NOMATCH, NULL, NULL); + sss_parse_name_check(test_ctx, "@", ERR_REGEX_NOMATCH, NULL, NULL); + sss_parse_name_check(test_ctx, "\\", ERR_REGEX_NOMATCH, NULL, NULL); + sss_parse_name_check(test_ctx, "\\"NAME, ERR_REGEX_NOMATCH, NULL, NULL); + sss_parse_name_check(test_ctx, "@"NAME, ERR_REGEX_NOMATCH, NULL, NULL); + sss_parse_name_check(test_ctx, NAME"@", ERR_REGEX_NOMATCH, NULL, NULL); + sss_parse_name_check(test_ctx, NAME"\\", ERR_REGEX_NOMATCH, NULL, NULL); +} + +void sss_parse_name_fail_default(void **state) +{ + struct parse_name_test_ctx *test_ctx = talloc_get_type(*state, + struct parse_name_test_ctx); + + sss_parse_name_check(test_ctx, "", ERR_REGEX_NOMATCH, NULL, NULL); + sss_parse_name_check(test_ctx, "@", ERR_REGEX_NOMATCH, NULL, NULL); + sss_parse_name_check(test_ctx, "@"NAME, ERR_REGEX_NOMATCH, NULL, NULL); + sss_parse_name_check(test_ctx, NAME"@", ERR_REGEX_NOMATCH, NULL, NULL); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_default, + fqdn_test_setup, fqdn_test_teardown), + cmocka_unit_test_setup_teardown(test_all, + fqdn_test_setup, fqdn_test_teardown), + cmocka_unit_test_setup_teardown(test_flat, + fqdn_test_setup, fqdn_test_teardown), + cmocka_unit_test_setup_teardown(test_flat_fallback, + fqdn_test_setup, fqdn_test_teardown), + cmocka_unit_test_setup_teardown(test_init_nouser, + fqdn_test_setup, fqdn_test_teardown), + + cmocka_unit_test_setup_teardown(parse_name_plain, + parse_name_test_setup_ipa_ad, + parse_name_test_teardown), + cmocka_unit_test_setup_teardown(parse_name_fqdn, + parse_name_test_setup_ipa_ad, + parse_name_test_teardown), + cmocka_unit_test_setup_teardown(parse_name_sub, + parse_name_test_setup_ipa_ad, + parse_name_test_teardown), + cmocka_unit_test_setup_teardown(parse_name_flat, + parse_name_test_setup_ipa_ad, + parse_name_test_teardown), + cmocka_unit_test_setup_teardown(parse_name_default, + parse_name_test_setup_ipa_ad, + parse_name_test_teardown), + cmocka_unit_test_setup_teardown(sss_parse_name_fail_ipa_ad, + parse_name_test_setup_ipa_ad, + parse_name_test_teardown), + cmocka_unit_test_setup_teardown(sss_parse_name_fail_default, + parse_name_test_setup_default, + parse_name_test_teardown), + cmocka_unit_test_setup_teardown(test_different_regexps, + parse_name_test_two_names_ctx_setup, + parse_name_test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_ifp.c b/src/tests/cmocka/test_ifp.c new file mode 100644 index 0000000..fd754e7 --- /dev/null +++ b/src/tests/cmocka/test_ifp.c @@ -0,0 +1,306 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: InfoPipe responder + + 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 <popt.h> +#include <dbus/dbus.h> + +#include "db/sysdb.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" +#include "responder/ifp/ifp_private.h" + +/* dbus library checks for valid object paths when unit testing, we don't + * want that */ +#undef DBUS_TYPE_OBJECT_PATH +#define DBUS_TYPE_OBJECT_PATH ((int) 's') + +void test_el_to_dict(void **state) +{ + TALLOC_CTX *tmp_ctx; + DBusMessage *message; + dbus_bool_t dbret; + DBusMessageIter iter; + DBusMessageIter iter_dict; + struct ldb_message_element *el; + errno_t ret; + char *attr_name; + char *attr_val; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + + message = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL); + assert_non_null(message); + + el = talloc(tmp_ctx, struct ldb_message_element); + assert_non_null(el); + el->name = "numbers"; + el->values = talloc_array(el, struct ldb_val, 2); + assert_non_null(el->values); + el->num_values = 2; + el->values[0].data = (uint8_t *) discard_const("one"); + el->values[0].length = strlen("one") + 1; + el->values[1].data = (uint8_t *) discard_const("two"); + el->values[1].length = strlen("two") + 1; + + dbus_message_iter_init_append(message, &iter); + dbret = dbus_message_iter_open_container( + &iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &iter_dict); + assert_true(dbret == TRUE); + + ret = ifp_add_ldb_el_to_dict(&iter_dict, el); + assert_int_equal(ret, EOK); + + dbret = dbus_message_iter_close_container(&iter, &iter_dict); + assert_true(dbret == TRUE); + + /* Test the reply contains what we expect */ + dbus_message_iter_init(message, &iter); + assert_int_equal(dbus_message_iter_get_arg_type(&iter), + DBUS_TYPE_ARRAY); + dbus_message_iter_recurse(&iter, &iter); + assert_int_equal(dbus_message_iter_get_arg_type(&iter), + DBUS_TYPE_DICT_ENTRY); + + dbus_message_iter_recurse(&iter, &iter_dict); + dbus_message_iter_get_basic(&iter_dict, &attr_name); + assert_string_equal(attr_name, "numbers"); + + dbus_message_iter_next(&iter_dict); + assert_int_equal(dbus_message_iter_get_arg_type(&iter_dict), + DBUS_TYPE_VARIANT); + dbus_message_iter_recurse(&iter_dict, &iter_dict); + assert_int_equal(dbus_message_iter_get_arg_type(&iter_dict), + DBUS_TYPE_ARRAY); + + dbus_message_iter_recurse(&iter_dict, &iter_dict); + dbus_message_iter_get_basic(&iter_dict, &attr_val); + assert_string_equal(attr_val, "one"); + assert_true(dbus_message_iter_next(&iter_dict)); + dbus_message_iter_get_basic(&iter_dict, &attr_val); + assert_string_equal(attr_val, "two"); + assert_false(dbus_message_iter_next(&iter_dict)); + + talloc_free(tmp_ctx); +} + +static void assert_string_list_equal(const char **s1, + const char **s2) +{ + int i; + + for (i=0; s1[i]; i++) { + assert_non_null(s2[i]); + assert_string_equal(s1[i], s2[i]); + } + + assert_null(s2[i]); +} + +static void attr_parse_test(const char *expected[], const char *input) +{ + const char **res; + TALLOC_CTX *test_ctx; + + test_ctx = talloc_new(NULL); + assert_non_null(test_ctx); + + res = ifp_parse_user_attr_list(test_ctx, input); + + if (expected) { + /* Positive test */ + assert_non_null(res); + assert_string_list_equal(res, expected); + } else { + /* Negative test */ + assert_null(res); + } + + talloc_free(test_ctx); +} + +static void attr_parse_test_ex(const char *expected[], const char *input, + const char **defaults) +{ + const char **res; + TALLOC_CTX *test_ctx; + + test_ctx = talloc_new(NULL); + assert_non_null(test_ctx); + + res = parse_attr_list_ex(test_ctx, input, defaults); + + if (expected) { + /* Positive test */ + assert_non_null(res); + assert_string_list_equal(res, expected); + } else { + /* Negative test */ + assert_null(res); + } + + talloc_free(test_ctx); +} + +void test_attr_acl(void **state) +{ + /* Test defaults */ + const char *exp_defaults[] = { SYSDB_NAME, SYSDB_UIDNUM, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, SYSDB_SHELL, + "groups", "domain", "domainname", + "extraAttributes", NULL }; + attr_parse_test(exp_defaults, NULL); + + /* Test adding some attributes to the defaults */ + const char *exp_add[] = { "telephoneNumber", "streetAddress", + SYSDB_NAME, SYSDB_UIDNUM, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, SYSDB_SHELL, + "groups", "domain", "domainname", + "extraAttributes", NULL }; + attr_parse_test(exp_add, "+telephoneNumber, +streetAddress"); + + /* Test removing some attributes to the defaults */ + const char *exp_rm[] = { SYSDB_NAME, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, "groups", + "domain", "domainname", + "extraAttributes", NULL }; + attr_parse_test(exp_rm, "-"SYSDB_SHELL ",-"SYSDB_UIDNUM); + + /* Test both add and remove */ + const char *exp_add_rm[] = { "telephoneNumber", + SYSDB_NAME, SYSDB_UIDNUM, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, "groups", + "domain", "domainname", + "extraAttributes", NULL }; + attr_parse_test(exp_add_rm, "+telephoneNumber, -"SYSDB_SHELL); + + /* Test rm trumps add */ + const char *exp_add_rm_override[] = { SYSDB_NAME, SYSDB_UIDNUM, + SYSDB_GIDNUM, SYSDB_GECOS, + SYSDB_HOMEDIR, SYSDB_SHELL, + "groups", "domain", + "domainname", + "extraAttributes", NULL }; + attr_parse_test(exp_add_rm_override, + "+telephoneNumber, -telephoneNumber, +telephoneNumber"); + + /* Remove all */ + const char *rm_all[] = { NULL }; + attr_parse_test(rm_all, "-"SYSDB_NAME ", -"SYSDB_UIDNUM + ", -"SYSDB_GIDNUM ", -"SYSDB_GECOS + ", -"SYSDB_HOMEDIR ", -"SYSDB_SHELL", -groups, " + "-domain, -domainname, -extraAttributes"); + + /* Malformed list */ + attr_parse_test(NULL, "missing_plus_or_minus"); +} + +void test_attr_acl_ex(void **state) +{ + /* Test defaults */ + const char *exp_defaults[] = { "abc", "123", "xyz", NULL }; + attr_parse_test_ex(exp_defaults, NULL, exp_defaults); + + /* Test adding some attributes to the defaults */ + const char *exp_add[] = { "telephoneNumber", "streetAddress", + "abc", "123", "xyz", + NULL }; + attr_parse_test_ex(exp_add, "+telephoneNumber, +streetAddress", + exp_defaults); + + /* Test removing some attributes to the defaults */ + const char *exp_rm[] = { "123", NULL }; + attr_parse_test_ex(exp_rm, "-abc, -xyz", exp_defaults); + + /* Test adding with empty defaults */ + const char *exp_add_empty[] = { "telephoneNumber", "streetAddress", + NULL }; + attr_parse_test_ex(exp_add_empty, "+telephoneNumber, +streetAddress", NULL); + + /* Test removing with empty defaults */ + const char *rm_all[] = { NULL }; + attr_parse_test_ex(rm_all, "-telephoneNumber, -streetAddress", NULL); +} + +void test_attr_allowed(void **state) +{ + const char *whitelist[] = { "name", "gecos", NULL }; + const char *emptylist[] = { NULL }; + + assert_true(ifp_attr_allowed(whitelist, "name")); + assert_true(ifp_attr_allowed(whitelist, "gecos")); + + assert_false(ifp_attr_allowed(whitelist, "password")); + + assert_false(ifp_attr_allowed(emptylist, "name")); + assert_false(ifp_attr_allowed(NULL, "name")); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_el_to_dict), + cmocka_unit_test(test_attr_acl), + cmocka_unit_test(test_attr_acl_ex), + cmocka_unit_test(test_attr_allowed), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_inotify.c b/src/tests/cmocka/test_inotify.c new file mode 100644 index 0000000..cd507a3 --- /dev/null +++ b/src/tests/cmocka/test_inotify.c @@ -0,0 +1,582 @@ +/* + Copyright (C) 2016 Red Hat + + 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 <errno.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <talloc.h> +#include <popt.h> + +#include "limits.h" +#include "shared/io.h" +#include "util/inotify.h" +#include "util/util.h" +#include "tests/common.h" + +struct inotify_test_ctx { + char *filename; + char *dirname; + + int ncb; + int threshold; + /* if the cb receives flags not in this set, test fails */ + uint32_t exp_flags; + + struct sss_test_ctx *tctx; + struct tevent_timer *fail_te; +}; + +static void test_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *ptr) +{ + DEBUG(SSSDBG_FATAL_FAILURE, "The test timed out!\n"); + talloc_free(te); + fail(); +} + +static struct inotify_test_ctx *common_setup(TALLOC_CTX *mem_ctx) +{ + struct inotify_test_ctx *ctx; + struct timeval tv; + + ctx = talloc_zero(mem_ctx, struct inotify_test_ctx); + if (ctx == NULL) { + return NULL; + } + + ctx->tctx = create_ev_test_ctx(ctx); + if (ctx->tctx == NULL) { + talloc_free(ctx); + return NULL; + } + + gettimeofday(&tv, NULL); + tv.tv_sec += 5; + ctx->fail_te = tevent_add_timer(ctx->tctx->ev, ctx, + tv, test_timeout, ctx); + if (ctx->fail_te == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue fallback timer!\n"); + talloc_free(ctx); + return NULL; + } + + return ctx; +} + +static int inotify_test_setup(void **state) +{ + struct inotify_test_ctx *ctx; + int fd; + + ctx = common_setup(NULL); + if (ctx == NULL) { + return 1; + } + + ctx->filename = talloc_strdup(ctx, "test_inotify.XXXXXX"); + if (ctx->filename == NULL) { + talloc_free(ctx); + return 1; + } + + fd = mkstemp(ctx->filename); + if (fd == -1) { + talloc_free(ctx); + return 1; + } + close(fd); + + *state = ctx; + return 0; +} + +static int inotify_test_dir_setup(void **state) +{ + struct inotify_test_ctx *ctx; + + ctx = common_setup(NULL); + if (ctx == NULL) { + return 1; + } + + ctx->dirname = talloc_strdup(ctx, "test_inotify_dir.XXXXXX"); + if (ctx->dirname == NULL) { + talloc_free(ctx); + return 1; + } + + ctx->dirname = mkdtemp(ctx->dirname); + if (ctx->dirname == NULL) { + talloc_free(ctx); + return 1; + } + + ctx->filename = talloc_asprintf(ctx, "%s/testfile", ctx->dirname); + if (ctx->filename == NULL) { + talloc_free(ctx); + return 1; + } + + *state = ctx; + return 0; +} + +static int inotify_test_teardown(void **state) +{ + struct inotify_test_ctx *ctx = talloc_get_type_abort(*state, + struct inotify_test_ctx); + int ret; + + ret = unlink(ctx->filename); + if (ret == -1 && errno != ENOENT) { + return 1; + } + + talloc_free(ctx); + return 0; +} + +static int inotify_test_dir_teardown(void **state) +{ + struct inotify_test_ctx *ctx = talloc_get_type_abort(*state, + struct inotify_test_ctx); + int ret; + + ret = unlink(ctx->filename); + if (ret == -1 && errno != ENOENT) { + return 1; + } + + ret = rmdir(ctx->dirname); + if (ret == -1 && errno != ENOENT) { + return 1; + } + + talloc_free(ctx); + return 0; +} + +static void file_mod_op(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *ptr) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, + struct inotify_test_ctx); + FILE *f; + + talloc_free(te); + + f = fopen(test_ctx->filename, "w"); + if (f == NULL) { + test_ctx->tctx->error = errno; + test_ctx->tctx->done = true; + return; + } + + fprintf(f, "%s\n", test_ctx->filename); + fflush(f); + fclose(f); +} + +static void check_and_set_threshold(struct inotify_test_ctx *test_ctx, + uint32_t flags) +{ + if (test_ctx->exp_flags != 0 && !(test_ctx->exp_flags & flags)) { + fail(); + } + + test_ctx->ncb++; +} + +static int inotify_set_threshold_cb(const char *filename, + uint32_t flags, + void *pvt) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(pvt, + struct inotify_test_ctx); + + check_and_set_threshold(test_ctx, flags); + return EOK; +} + +static int inotify_threshold_cb(const char *filename, + uint32_t flags, + void *pvt) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(pvt, + struct inotify_test_ctx); + + check_and_set_threshold(test_ctx, flags); + if (test_ctx->ncb == test_ctx->threshold) { + test_ctx->tctx->done = true; + return EOK; + } + + return EOK; +} + +/* Test that running two modifications fires the callback twice */ +static void test_inotify_mod(void **state) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct inotify_test_ctx); + struct snotify_ctx *ctx; + struct timeval tv; + struct tevent_timer *te; + errno_t ret; + + ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, + test_ctx->filename, NULL, IN_MODIFY, + inotify_threshold_cb, test_ctx); + assert_non_null(ctx); + + test_ctx->threshold = 2; + test_ctx->exp_flags = IN_MODIFY; + + gettimeofday(&tv, NULL); + tv.tv_usec += 500; + te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, + tv, file_mod_op, test_ctx); + if (te == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); + return; + } + + gettimeofday(&tv, NULL); + tv.tv_sec += 1; + te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, + tv, file_mod_op, test_ctx); + if (te == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); + return; + } + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, EOK); + + talloc_free(ctx); +} + +static void file_mv_op(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *ptr) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, + struct inotify_test_ctx); + FILE *f; + int fd; + char src_tmp_file[] = "test_inotify_src.XXXXXX"; + int ret; + + talloc_free(te); + + fd = mkstemp(src_tmp_file); + if (fd == -1) { + test_ctx->tctx->error = errno; + test_ctx->tctx->done = true; + return; + } + + f = fdopen(fd, "w"); + if (f == NULL) { + close(fd); + unlink(src_tmp_file); + test_ctx->tctx->error = errno; + test_ctx->tctx->done = true; + return; + } + + fprintf(f, "%s\n", test_ctx->filename); + fflush(f); + fclose(f); + + ret = rename(src_tmp_file, test_ctx->filename); + if (ret == -1) { + unlink(src_tmp_file); + test_ctx->tctx->error = errno; + test_ctx->tctx->done = true; + return; + } +} + +static void test_inotify_mv(void **state) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct inotify_test_ctx); + struct snotify_ctx *ctx; + struct timeval tv; + struct tevent_timer *te; + errno_t ret; + + ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, + test_ctx->filename, NULL, IN_MOVED_TO, + inotify_threshold_cb, test_ctx); + assert_non_null(ctx); + + test_ctx->threshold = 1; + test_ctx->exp_flags = IN_MOVED_TO; + + gettimeofday(&tv, NULL); + tv.tv_usec += 200; + te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, + tv, file_mv_op, test_ctx); + if (te == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); + return; + } + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static void file_del_add_op(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *ptr) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, + struct inotify_test_ctx); + FILE *f; + int ret; + + talloc_free(te); + + ret = unlink(test_ctx->filename); + if (ret == -1) { + test_ctx->tctx->error = errno; + test_ctx->tctx->done = true; + return; + } + + f = fopen(test_ctx->filename, "w"); + if (f == NULL) { + test_ctx->tctx->error = errno; + test_ctx->tctx->done = true; + return; + } + + fprintf(f, "%s\n", test_ctx->filename); + fflush(f); + fclose(f); +} + +static void test_inotify_del_add(void **state) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct inotify_test_ctx); + struct snotify_ctx *ctx; + struct timeval tv; + struct tevent_timer *te; + errno_t ret; + + test_ctx->threshold = 1; + test_ctx->exp_flags = IN_CREATE; + + ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, + test_ctx->filename, NULL, + IN_CREATE, + inotify_threshold_cb, test_ctx); + assert_non_null(ctx); + + gettimeofday(&tv, NULL); + tv.tv_usec += 200; + te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, + tv, file_del_add_op, test_ctx); + if (te == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); + return; + } + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static void test_inotify_file_moved_in(void **state) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct inotify_test_ctx); + struct snotify_ctx *ctx; + struct timeval tv; + struct tevent_timer *te; + errno_t ret; + + test_ctx->threshold = 1; + test_ctx->exp_flags = IN_CREATE; + + ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, + test_ctx->filename, NULL, + IN_CREATE | IN_CLOSE_WRITE, + inotify_threshold_cb, test_ctx); + assert_non_null(ctx); + + gettimeofday(&tv, NULL); + tv.tv_usec += 200; + + te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, + tv, file_mod_op, test_ctx); + if (te == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); + return; + } + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static void file_del_op(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *ptr) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, + struct inotify_test_ctx); + int ret; + + talloc_free(te); + + ret = unlink(test_ctx->filename); + if (ret == -1) { + test_ctx->tctx->error = errno; + test_ctx->tctx->done = true; + return; + } +} + +static void check_threshold_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *ptr) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(ptr, + struct inotify_test_ctx); + + /* tests that no more callbacks were issued and exactly one + * was caught for both requests + */ + if (test_ctx->ncb == test_ctx->threshold) { + test_ctx->tctx->done = true; + return; + } + + fail(); +} + +static void test_inotify_delay(void **state) +{ + struct inotify_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct inotify_test_ctx); + struct snotify_ctx *ctx; + struct timeval tv; + struct tevent_timer *te; + errno_t ret; + struct timeval delay = { .tv_sec = 1, .tv_usec = 0 }; + + test_ctx->threshold = 1; + test_ctx->exp_flags = IN_CREATE | IN_DELETE; + + ctx = snotify_create(test_ctx, test_ctx->tctx->ev, SNOTIFY_WATCH_DIR, + test_ctx->filename, &delay, + IN_CREATE | IN_DELETE, + inotify_set_threshold_cb, test_ctx); + assert_non_null(ctx); + + gettimeofday(&tv, NULL); + tv.tv_usec += 100; + te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, + tv, file_mod_op, test_ctx); + if (te == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); + return; + } + + gettimeofday(&tv, NULL); + tv.tv_usec += 200; + te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, + tv, file_del_op, test_ctx); + if (te == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); + return; + } + + gettimeofday(&tv, NULL); + tv.tv_sec += 2; + te = tevent_add_timer(test_ctx->tctx->ev, test_ctx, + tv, check_threshold_cb, test_ctx); + if (te == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to queue file update!\n"); + return; + } + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_inotify_mv, + inotify_test_setup, + inotify_test_teardown), + cmocka_unit_test_setup_teardown(test_inotify_mod, + inotify_test_setup, + inotify_test_teardown), + cmocka_unit_test_setup_teardown(test_inotify_del_add, + inotify_test_setup, + inotify_test_teardown), + cmocka_unit_test_setup_teardown(test_inotify_file_moved_in, + inotify_test_dir_setup, + inotify_test_dir_teardown), + cmocka_unit_test_setup_teardown(test_inotify_delay, + inotify_test_dir_setup, + inotify_test_dir_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_io.c b/src/tests/cmocka/test_io.c new file mode 100644 index 0000000..20475a0 --- /dev/null +++ b/src/tests/cmocka/test_io.c @@ -0,0 +1,243 @@ +/* + SSSD + + find_uid - Utilities tests + + Authors: + Abhishek Singh <abhishekkumarsingh.cse@gmail.com> + + Copyright (C) 2013 Red Hat + + 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 <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <dirent.h> +#include <unistd.h> +#include <libgen.h> + +#include "limits.h" +#include "shared/io.h" +#include "util/util.h" +#include "tests/common.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define FILE_TEMPLATE TESTS_PATH"/test_io.XXXXXX" +#define NON_EX_PATH TESTS_PATH"/non-existent-path" + +/* Creates a unique temporary file inside TEST_DIR and returns its path*/ +static char *get_random_filepath(const char *template) +{ + int ret; + char *path; + + path = strdup(template); + assert_non_null(path); + + ret = mkstemp(path); + if (ret == -1) { + int err = errno; + fprintf(stderr, "mkstemp failed with path:'%s' [%s]\n", + path, strerror(err)); + } + assert_int_not_equal(ret, -1); + + /* We do not need this file descriptor */ + close(ret); + + return path; +} + +static int test_file_setup(void **state) +{ + int ret; + char *file_path; + + ret = mkdir(TESTS_PATH, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + assert_int_equal(ret, EOK); + + file_path = get_random_filepath(FILE_TEMPLATE); + assert_non_null(file_path); + + ret = unlink(NON_EX_PATH); + ret = errno; + assert_int_equal(ret, ENOENT); + + *state = file_path; + return 0; +} + +static int test_file_teardown(void **state) +{ + int ret; + char *file_path = (char *)*state; + + ret = unlink(file_path); + assert_int_equal(ret, EOK); + free(file_path); + + ret = rmdir(TESTS_PATH); + assert_int_equal(ret, EOK); + return 0; +} + +struct dir_state { + int dir_fd; + char *basename; + + /* resources for cleanup*/ + DIR *dirp; + char *filename; +}; + +static int test_dir_setup(void **state) +{ + struct dir_state *data; + int ret; + + data = (struct dir_state *)calloc(1, sizeof(struct dir_state)); + assert_non_null(data); + + ret = mkdir(TESTS_PATH, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + assert_int_equal(ret, EOK); + + data->dirp = opendir(TESTS_PATH); + if (data->dirp == NULL) { + int err = errno; + fprintf(stderr, "Could not open directory:'%s' [%s]\n", + TESTS_PATH, strerror(err)); + } + assert_non_null(data->dirp); + + data->dir_fd = dirfd(data->dirp); + assert_int_not_equal(data->dir_fd, -1); + + data->filename = get_random_filepath(FILE_TEMPLATE); + assert_non_null(data->filename); + + data->basename = basename(data->filename); + + ret = unlink(NON_EX_PATH); + ret = errno; + assert_int_equal(ret, ENOENT); + + *state = data; + return 0; +} + +static int test_dir_teardown(void **state) +{ + int ret; + struct dir_state *data = (struct dir_state *) *state; + + ret = unlink(data->filename); + assert_int_equal(ret, EOK); + free(data->filename); + + ret = closedir(data->dirp); + assert_int_equal(ret, EOK); + + ret = rmdir(TESTS_PATH); + assert_int_equal(ret, EOK); + + free(data); + return 0; +} + +void test_sss_open_cloexec_success(void **state) +{ + int fd; + int ret; + int ret_flag; + int expec_flag; + int flags = O_RDWR; + char *file_path = (char *) *state; + + fd = sss_open_cloexec(file_path, flags, &ret); + assert_int_not_equal(fd, -1); + + ret_flag = fcntl(fd, F_GETFD, 0); + expec_flag = FD_CLOEXEC; + assert_true(ret_flag & expec_flag); + + close(fd); +} + +void test_sss_open_cloexec_fail(void **state) +{ + int fd; + int ret; + int flags = O_RDWR; + + fd = sss_open_cloexec(NON_EX_PATH, flags, &ret); + + assert_true(fd == -1); + assert_int_not_equal(ret, 0); +} + +void test_sss_openat_cloexec_success(void **state) +{ + int fd; + int ret; + int ret_flag; + int expec_flag; + const int flags = O_RDWR; + struct dir_state *data = (struct dir_state *) *state; + + fd = sss_openat_cloexec(data->dir_fd, data->basename, flags, &ret); + assert_int_not_equal(fd, -1); + + ret_flag = fcntl(fd, F_GETFD, 0); + expec_flag = FD_CLOEXEC; + assert_true(ret_flag & expec_flag); + + close(fd); +} + +void test_sss_openat_cloexec_fail(void **state) +{ + int fd; + int ret; + int flags = O_RDWR; + struct dir_state *data = (struct dir_state *) *state; + + fd = sss_openat_cloexec(data->dir_fd, NON_EX_PATH, flags, &ret); + assert_int_equal(fd, -1); + assert_int_equal(ret, ENOENT); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sss_open_cloexec_success, + test_file_setup, test_file_teardown), + cmocka_unit_test_setup_teardown(test_sss_open_cloexec_fail, + test_file_setup, test_file_teardown), + cmocka_unit_test_setup_teardown(test_sss_openat_cloexec_success, + test_dir_setup, test_dir_teardown), + cmocka_unit_test_setup_teardown(test_sss_openat_cloexec_fail, + test_dir_setup, test_dir_teardown) + }; + + tests_set_cwd(); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_iobuf.c b/src/tests/cmocka/test_iobuf.c new file mode 100644 index 0000000..796db51 --- /dev/null +++ b/src/tests/cmocka/test_iobuf.c @@ -0,0 +1,195 @@ +/* + SSSD + + test_iobuf - IO buffer tests + + Copyright (C) 2016 Red Hat + + 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 <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "util/sss_iobuf.h" +#include "util/util.h" + +static void test_sss_iobuf_read(void **state) +{ + errno_t ret; + uint8_t buffer[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0 }; + uint8_t readbuf[64] = { 0 }; + size_t nread; + struct sss_iobuf *rb; + + rb = sss_iobuf_init_readonly(NULL, buffer, sizeof(buffer)); + assert_non_null(rb); + + ret = sss_iobuf_read(rb, 5, readbuf, &nread); + assert_int_equal(ret, EOK); + /* There is enough data in the buffer */ + assert_int_equal(nread, 5); + /* The data matches beginning of the buffer */ + assert_int_equal(strncmp((const char *) readbuf, "Hello", 5), 0); + + memset(readbuf, 0, sizeof(readbuf)); + ret = sss_iobuf_read(rb, 3, readbuf, &nread); + assert_int_equal(ret, EOK); + /* There is enough data in the buffer */ + assert_int_equal(nread, 3); + /* The data matches beginning of the buffer */ + assert_int_equal(strncmp((const char *) readbuf, " wo", 3), 0); + + /* Try to read more than the buffer has */ + memset(readbuf, 0, sizeof(readbuf)); + ret = sss_iobuf_read(rb, 10, readbuf, &nread); + /* This is not a fatal error */ + assert_int_equal(ret, EOK); + /* We just see how much there was */ + assert_int_equal(nread, 4); + /* And get the rest of the buffer back. readbuf includes trailing zero now */ + assert_int_equal(strcmp((const char *) readbuf, "rld"), 0); + + /* Reading a depleted buffer will just yield zero bytes read now */ + ret = sss_iobuf_read(rb, 10, readbuf, &nread); + assert_int_equal(ret, EOK); + assert_int_equal(nread, 0); + + /* Failure cases */ + ret = sss_iobuf_read(NULL, 10, readbuf, &nread); + assert_int_equal(ret, EINVAL); + ret = sss_iobuf_read(rb, 10, NULL, &nread); + assert_int_equal(ret, EINVAL); + + talloc_free(rb); +} + +static void test_sss_iobuf_write(void **state) +{ + struct sss_iobuf *wb; + struct sss_iobuf *rb; + size_t hwlen = sizeof("Hello world"); /* Includes trailing zero */ + uint8_t readbuf[64]; + size_t nread; + errno_t ret; + + /* Exactly fill the capacity */ + wb = sss_iobuf_init_empty(NULL, hwlen, hwlen); + assert_non_null(wb); + ret = sss_iobuf_write_len(wb, + (uint8_t *) discard_const("Hello world"), + sizeof("Hello world")); + assert_int_equal(ret, EOK); + + rb = sss_iobuf_init_readonly(NULL, + sss_iobuf_get_data(wb), + sss_iobuf_get_len(wb)); + talloc_free(wb); + assert_non_null(rb); + + ret = sss_iobuf_read(rb, sizeof(readbuf), readbuf, &nread); + assert_int_equal(ret, EOK); + assert_int_equal(nread, hwlen); + assert_int_equal(strcmp((const char *) readbuf, "Hello world"), 0); + talloc_zfree(rb); + + /* Overflow the capacity by one */ + wb = sss_iobuf_init_empty(NULL, hwlen, hwlen); + assert_non_null(wb); + ret = sss_iobuf_write_len(wb, + (uint8_t *) discard_const("Hello world!"), + sizeof("Hello world!")); + assert_int_not_equal(ret, EOK); + talloc_zfree(wb); + + /* Test resizing exactly up to capacity in several writes */ + wb = sss_iobuf_init_empty(NULL, 2, hwlen); + assert_non_null(wb); + + ret = sss_iobuf_write_len(wb, + (uint8_t *) discard_const("Hello "), + sizeof("Hello ")-1); /* Not the null byte now.. */ + assert_int_equal(ret, EOK); + ret = sss_iobuf_write_len(wb, + (uint8_t *) discard_const("world"), + sizeof("world")); + assert_int_equal(ret, EOK); + + rb = sss_iobuf_init_readonly(NULL, + sss_iobuf_get_data(wb), + sss_iobuf_get_len(wb)); + talloc_free(wb); + assert_non_null(rb); + + ret = sss_iobuf_read(rb, sizeof(readbuf), readbuf, &nread); + assert_int_equal(ret, EOK); + assert_int_equal(nread, hwlen); + assert_int_equal(strcmp((const char *) readbuf, "Hello world"), 0); + talloc_zfree(rb); + + /* Overflow the capacity during a resize by one */ + wb = sss_iobuf_init_empty(NULL, 2, hwlen); + assert_non_null(wb); + + ret = sss_iobuf_write_len(wb, + (uint8_t *) discard_const("Hello "), + sizeof("Hello ")-1); /* Not the null byte now.. */ + assert_int_equal(ret, EOK); + ret = sss_iobuf_write_len(wb, + (uint8_t *) discard_const("world!"), + sizeof("world!")); + assert_int_not_equal(ret, EOK); + talloc_zfree(wb); + + /* Test allocating an unlimited buffer */ + wb = sss_iobuf_init_empty(NULL, 2, 0); + assert_non_null(wb); + + ret = sss_iobuf_write_len(wb, + (uint8_t *) discard_const("Hello "), + sizeof("Hello ")-1); /* Not the null byte now.. */ + assert_int_equal(ret, EOK); + ret = sss_iobuf_write_len(wb, + (uint8_t *) discard_const("world"), + sizeof("world")); + assert_int_equal(ret, EOK); + + rb = sss_iobuf_init_readonly(NULL, + sss_iobuf_get_data(wb), + sss_iobuf_get_len(wb)); + talloc_free(wb); + assert_non_null(rb); + + ret = sss_iobuf_read(rb, sizeof(readbuf), readbuf, &nread); + assert_int_equal(ret, EOK); + assert_int_equal(nread, hwlen); + assert_int_equal(strcmp((const char *) readbuf, "Hello world"), 0); + talloc_zfree(rb); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sss_iobuf_read), + cmocka_unit_test(test_sss_iobuf_write), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_ipa_dn.c b/src/tests/cmocka/test_ipa_dn.c new file mode 100644 index 0000000..b39e05b --- /dev/null +++ b/src/tests/cmocka/test_ipa_dn.c @@ -0,0 +1,235 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2015 Red Hat + + 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 <talloc.h> +#include <errno.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "providers/ipa/ipa_dn.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_ipa_dn_conf.ldb" +#define TEST_DOM_NAME "ipa_dn_test" +#define TEST_ID_PROVIDER "ipa" + +struct ipa_dn_test_ctx { + struct sss_test_ctx *tctx; + struct sysdb_ctx *sysdb; +}; + +static int ipa_dn_test_setup(void **state) +{ + struct ipa_dn_test_ctx *test_ctx = NULL; + + test_ctx = talloc_zero(NULL, struct ipa_dn_test_ctx); + assert_non_null(test_ctx); + *state = test_ctx; + + /* initialize domain */ + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, + TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + test_ctx->sysdb = test_ctx->tctx->sysdb; + + return 0; +} + +static int ipa_dn_test_teardown(void **state) +{ + talloc_zfree(*state); + return 0; +} + +static void ipa_check_rdn_test(void **state) +{ + struct ipa_dn_test_ctx *test_ctx = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct ipa_dn_test_ctx); + + ret = ipa_check_rdn(test_ctx->sysdb, "cn=rdn,dc=example,dc=com", "cn"); + assert_int_equal(ret, EOK); + + ret = ipa_check_rdn(test_ctx->sysdb, "cn=rdn,attr1=value1,dc=example,dc=com", "cn", "attr1", "value1"); + assert_int_equal(ret, EOK); + + ret = ipa_check_rdn(test_ctx->sysdb, "cn=rdn,attr1=value1,attr2=value2,dc=example,dc=com", "cn", "attr1", "value1", "attr2", "value2"); + assert_int_equal(ret, EOK); + + ret = ipa_check_rdn(test_ctx->sysdb, "cn=rdn,dc=example,dc=com", "nope"); + assert_int_equal(ret, ENOENT); + + ret = ipa_check_rdn(test_ctx->sysdb, "cn=rdn,attr1=value1,dc=example,dc=com", "cn", "nope", "value1"); + assert_int_equal(ret, ENOENT); + + ret = ipa_check_rdn(test_ctx->sysdb, "cn=rdn,attr1=value1,attr2=value2,dc=example,dc=com", "cn", "attr1", "nope"); + assert_int_equal(ret, ENOENT); + + ret = ipa_check_rdn(test_ctx->sysdb, "cn=rdn,attr1=value1,dc=example,dc=com", "cn", "attr1"); + assert_int_equal(ret, ENOENT); + + ret = ipa_check_rdn(test_ctx->sysdb, "cn=rdn,attr1=value1", "cn", "attr1", "value1"); + assert_int_equal(ret, ENOENT); +} + +static void ipa_check_rdn_bool_test(void **state) +{ + struct ipa_dn_test_ctx *test_ctx = NULL; + bool bret; + + test_ctx = talloc_get_type_abort(*state, struct ipa_dn_test_ctx); + + bret = ipa_check_rdn_bool(test_ctx->sysdb, "cn=rdn,dc=example,dc=com", "cn"); + assert_true(bret); + + bret = ipa_check_rdn_bool(test_ctx->sysdb, "cn=rdn,attr1=value1,dc=example,dc=com", "cn", "attr1", "value1"); + assert_true(bret); + + bret = ipa_check_rdn_bool(test_ctx->sysdb, "cn=rdn,attr1=value1,attr2=value2,dc=example,dc=com", "cn", "attr1", "value1", "attr2", "value2"); + assert_true(bret); + + bret = ipa_check_rdn_bool(test_ctx->sysdb, "cn=rdn,dc=example,dc=com", "nope"); + assert_false(bret); + + bret = ipa_check_rdn_bool(test_ctx->sysdb, "cn=rdn,attr1=value1,dc=example,dc=com", "cn", "nope", "value1"); + assert_false(bret); + + bret = ipa_check_rdn_bool(test_ctx->sysdb, "cn=rdn,attr1=value1,attr2=value2,dc=example,dc=com", "cn", "attr1", "nope"); + assert_false(bret); + + bret = ipa_check_rdn_bool(test_ctx->sysdb, "cn=rdn,attr1=value1,dc=example,dc=com", "cn", "attr1"); + assert_false(bret); + + bret = ipa_check_rdn_bool(test_ctx->sysdb, "cn=rdn,attr1=value1", "cn", "attr1", "value1"); + assert_false(bret); +} + +static void ipa_get_rdn_test(void **state) +{ + struct ipa_dn_test_ctx *test_ctx = NULL; + const char *exprdn = "rdn"; + char *rdn = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct ipa_dn_test_ctx); + + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, "cn=rdn,dc=example,dc=com", &rdn, "cn"); + assert_int_equal(ret, EOK); + assert_non_null(rdn); + assert_string_equal(exprdn, rdn); + + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, "cn=rdn,attr1=value1,dc=example,dc=com", &rdn, "cn", "attr1", "value1"); + assert_int_equal(ret, EOK); + assert_non_null(rdn); + assert_string_equal(exprdn, rdn); + + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, "cn=rdn,attr1=value1,attr2=value2,dc=example,dc=com", &rdn, "cn", "attr1", "value1", "attr2", "value2"); + assert_int_equal(ret, EOK); + assert_non_null(rdn); + assert_string_equal(exprdn, rdn); + + rdn = NULL; + + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, "cn=rdn,dc=example,dc=com", &rdn, "nope"); + assert_int_equal(ret, ENOENT); + assert_null(rdn); + + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, "cn=rdn,attr1=value1,dc=example,dc=com", &rdn, "cn", "nope", "value1"); + assert_int_equal(ret, ENOENT); + assert_null(rdn); + + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, "cn=rdn,attr1=value1,attr2=value2,dc=example,dc=com", &rdn, "cn", "attr1", "nope"); + assert_int_equal(ret, ENOENT); + assert_null(rdn); + + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, "cn=rdn,attr1=value1,dc=example,dc=com", &rdn, "cn", "attr1"); + assert_int_equal(ret, ENOENT); + assert_null(rdn); + + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, "cn=rdn,attr1=value1", &rdn, "cn", "attr1", "value1"); + assert_int_equal(ret, ENOENT); + assert_null(rdn); + + ret = ipa_get_rdn(test_ctx, test_ctx->sysdb, + "cn=rdn+nsuniqueid=9b1e3301-c32611e6-bdcae37a-ef905e7c," + "attr1=value1,attr2=value2,dc=example,dc=com", + &rdn, "cn", "attr1", "value1", "attr2", "value2"); + assert_int_equal(ret, ENOENT); + assert_null(rdn); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(ipa_check_rdn_test, + ipa_dn_test_setup, + ipa_dn_test_teardown), + cmocka_unit_test_setup_teardown(ipa_check_rdn_bool_test, + ipa_dn_test_setup, + ipa_dn_test_teardown), + cmocka_unit_test_setup_teardown(ipa_get_rdn_test, + ipa_dn_test_setup, + ipa_dn_test_teardown) + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + return rv; +} diff --git a/src/tests/cmocka/test_ipa_idmap.c b/src/tests/cmocka/test_ipa_idmap.c new file mode 100644 index 0000000..4441827 --- /dev/null +++ b/src/tests/cmocka/test_ipa_idmap.c @@ -0,0 +1,356 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: Unit tests for id-mapping in the IPA provider + + 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 <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "lib/idmap/sss_idmap.h" +#include "providers/ipa/ipa_common.h" +#include "providers/ldap/sdap_idmap.h" + +#define RANGE_NAME discard_const("range1") +#define DOMAIN_SID discard_const("S-1-5-21-2-3-4") +#define DOMAIN_NAME discard_const("dom.test") +#define BASE_RID 111 +#define SECONDARY_BASE_RID 11223344 +#define BASE_ID 123456 +#define RANGE_SIZE 222222 +#define RANGE_MAX (BASE_ID + RANGE_SIZE - 1) + +void test_get_idmap_data_from_range(void **state) +{ + char *dom_name; + char *sid; + uint32_t rid; + struct sss_idmap_range range; + bool external_mapping; + size_t c; + errno_t ret; + + struct test_data { + struct range_info r; + errno_t exp_ret; + char *exp_dom_name; + char *exp_sid; + uint32_t exp_rid; + struct sss_idmap_range exp_range; + bool exp_external_mapping; + } d[] = { + /* working IPA_RANGE_LOCAL range */ + {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, SECONDARY_BASE_RID, + NULL, discard_const(IPA_RANGE_LOCAL), MPG_DEFAULT}, + EOK, DOMAIN_NAME, NULL, 0, {BASE_ID, RANGE_MAX}, true}, + /* working old-style IPA_RANGE_LOCAL range without range type */ + {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, SECONDARY_BASE_RID, + NULL, NULL, MPG_DEFAULT}, + EOK, DOMAIN_NAME, NULL, 0, {BASE_ID, RANGE_MAX}, true}, + /* old-style IPA_RANGE_LOCAL without SID and secondary base rid */ + {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, 0, NULL, NULL, + MPG_DEFAULT}, + EINVAL, NULL, NULL, 0, {0, 0}, false}, + /* old-style range with SID and secondary base rid */ + {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, SECONDARY_BASE_RID, + DOMAIN_SID, NULL, MPG_DEFAULT}, + EINVAL, NULL, NULL, 0, {0, 0}, false}, + /* working IPA_RANGE_AD_TRUST range */ + {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, 0, DOMAIN_SID, + discard_const(IPA_RANGE_AD_TRUST), MPG_DEFAULT}, + EOK, DOMAIN_SID, DOMAIN_SID, BASE_RID, {BASE_ID, RANGE_MAX}, false}, + /* working old-style IPA_RANGE_AD_TRUST range without range type */ + {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, 0, DOMAIN_SID, NULL, + MPG_DEFAULT}, + EOK, DOMAIN_SID, DOMAIN_SID, BASE_RID, {BASE_ID, RANGE_MAX}, false}, + /* working IPA_RANGE_AD_TRUST_POSIX range */ + {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, 0, DOMAIN_SID, + discard_const(IPA_RANGE_AD_TRUST_POSIX), MPG_DEFAULT}, + EOK, DOMAIN_SID, DOMAIN_SID, 0, {BASE_ID, RANGE_MAX}, true}, + /* IPA_RANGE_AD_TRUST range with unsupported type */ + {{RANGE_NAME, BASE_ID, RANGE_SIZE, BASE_RID, 0, DOMAIN_SID, + discard_const("unsupported-range"), MPG_DEFAULT}, + ERR_UNSUPPORTED_RANGE_TYPE, NULL, NULL, 0, {0, 0}, false}, + {{0}, 0, NULL, NULL, 0, {0, 0}, false} + }; + + for (c = 0; d[c].exp_dom_name != NULL || d[c].exp_ret != 0; c++) { + ret = get_idmap_data_from_range(&d[c].r, DOMAIN_NAME, &dom_name, &sid, + &rid, &range, &external_mapping); + assert_int_equal(ret, d[c].exp_ret); + if (ret == 0) { + assert_string_equal(dom_name, d[c].exp_dom_name); + if (d[c].exp_sid == NULL) { + assert_null(sid); + } else { + assert_string_equal(sid, d[c].exp_sid); + } + assert_int_equal(rid, d[c].exp_rid); + assert_int_equal(range.min, d[c].exp_range.min); + assert_int_equal(range.max, d[c].exp_range.max); + assert_true(external_mapping == d[c].exp_external_mapping); + } + } +} + +errno_t __wrap_sysdb_get_ranges(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, + size_t *range_count, + struct range_info ***range_list) +{ + + *range_count = sss_mock_type(size_t); + *range_list = talloc_steal(mem_ctx, + sss_mock_ptr_type(struct range_info **)); + return EOK; +} + +struct test_ctx { + struct sdap_idmap_ctx *idmap_ctx; + struct sdap_id_ctx *sdap_id_ctx; +}; + +static struct range_info **get_range_list(TALLOC_CTX *mem_ctx) +{ + struct range_info **range_list; + + range_list = talloc_array(mem_ctx, struct range_info *, 2); + assert_non_null(range_list); + + range_list[0] = talloc_zero(range_list, struct range_info); + assert_non_null(range_list[0]); + + range_list[0]->name = talloc_strdup(range_list[0], RANGE_NAME); + assert_non_null( range_list[0]->name); + range_list[0]->base_id = BASE_ID; + range_list[0]->id_range_size = RANGE_SIZE; + range_list[0]->base_rid = BASE_RID; + range_list[0]->secondary_base_rid = 0; + range_list[0]->trusted_dom_sid = talloc_strdup(range_list[0], DOMAIN_SID); + assert_non_null(range_list[0]->trusted_dom_sid); + range_list[0]->range_type = talloc_strdup(range_list[0], + IPA_RANGE_AD_TRUST); + assert_non_null(range_list[0]->range_type); + + return range_list; +} + +static int setup_idmap_ctx(void **state) +{ + int ret; + struct test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->sdap_id_ctx = talloc_zero(test_ctx, + struct sdap_id_ctx); + assert_non_null(test_ctx->sdap_id_ctx); + + test_ctx->sdap_id_ctx->be = talloc_zero(test_ctx->sdap_id_ctx, + struct be_ctx); + assert_non_null(test_ctx->sdap_id_ctx->be); + + test_ctx->sdap_id_ctx->be->domain = talloc_zero(test_ctx->sdap_id_ctx->be, + struct sss_domain_info); + assert_non_null(test_ctx->sdap_id_ctx->be->domain); + + test_ctx->sdap_id_ctx->be->domain->name = + talloc_strdup(test_ctx->sdap_id_ctx->be->domain, DOMAIN_NAME); + assert_non_null(test_ctx->sdap_id_ctx->be->domain->name); + + will_return(__wrap_sysdb_get_ranges, 1); + will_return(__wrap_sysdb_get_ranges, get_range_list(global_talloc_context)); + + ret = ipa_idmap_init(test_ctx, test_ctx->sdap_id_ctx, + &test_ctx->idmap_ctx); + assert_int_equal(ret, EOK); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int teardown_idmap_ctx(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + assert_true(check_leaks_pop(test_ctx) == true); + + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void test_ipa_idmap_get_ranges_from_sysdb(void **state) +{ + int ret; + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + assert_non_null(test_ctx); + + will_return(__wrap_sysdb_get_ranges, 1); + will_return(__wrap_sysdb_get_ranges, get_range_list(test_ctx->idmap_ctx)); + ret = ipa_idmap_get_ranges_from_sysdb(test_ctx->idmap_ctx, + DOMAIN_NAME, DOMAIN_SID, true); + assert_int_equal(ret, EOK); + + will_return(__wrap_sysdb_get_ranges, 1); + will_return(__wrap_sysdb_get_ranges, get_range_list(global_talloc_context)); + ret = ipa_idmap_get_ranges_from_sysdb(test_ctx->idmap_ctx, + DOMAIN_NAME, DOMAIN_SID, false); + assert_int_equal(ret, EIO); +} + +struct sysdb_attrs *create_range_attrs(TALLOC_CTX *mem_ctx, + struct range_info *r) +{ + int ret; + struct sysdb_attrs *a = NULL; + + a = sysdb_new_attrs(mem_ctx); + assert_non_null(a); + + ret = sysdb_attrs_add_string(a, IPA_CN, r->name); + + if (ret == 0) { + ret = sysdb_attrs_add_string(a, IPA_TRUSTED_DOMAIN_SID, + r->trusted_dom_sid); + } + + if (ret == 0) { + ret = sysdb_attrs_add_uint32(a, IPA_BASE_ID, r->base_id); + } + + if (ret == 0) { + ret = sysdb_attrs_add_uint32(a, IPA_ID_RANGE_SIZE, r->id_range_size); + } + + if (ret == 0) { + ret = sysdb_attrs_add_uint32(a, IPA_BASE_RID, r->base_rid); + } + + if (ret == 0) { + ret = sysdb_attrs_add_uint32(a, IPA_SECONDARY_BASE_RID, + r->secondary_base_rid); + } + + if (ret == 0) { + ret = sysdb_attrs_add_string(a, IPA_RANGE_TYPE, r->range_type); + } + + if (ret != 0) { + talloc_zfree(a); + } + + return a; + +} + + +void test_ipa_ranges_parse_results(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + assert_non_null(test_ctx); + int ret; + size_t count = 5; + size_t c; + size_t d; + struct sysdb_attrs *reply[5]; + struct range_info **range_list; + struct range_info r[5] = { + { discard_const("range1"), 1000, 500, 0, 1000, discard_const("S-1-2-1"), discard_const(IPA_RANGE_AD_TRUST), MPG_DEFAULT }, + { discard_const("range2"), 2000, 500, 0, 2000, discard_const("S-1-2-2"), discard_const(IPA_RANGE_AD_TRUST), MPG_DEFAULT }, + { discard_const("range3"), 3000, 500, 0, 3000, discard_const("S-1-2-3"), discard_const("unsupported-type"), MPG_DEFAULT }, + { discard_const("range4"), 4000, 500, 0, 4000, discard_const("S-1-2-4"), discard_const(IPA_RANGE_AD_TRUST), MPG_DEFAULT }, + { discard_const("range5"), 5000, 500, 0, 5000, discard_const("S-1-2-5"), discard_const(IPA_RANGE_AD_TRUST), MPG_DEFAULT } + }; + + for (c = 0; c < count; c++) { + reply[c] = create_range_attrs(test_ctx, &r[c]); + assert_non_null(reply[c]); + } + + ret = ipa_ranges_parse_results(test_ctx, discard_const("mydom"), + count, reply, &range_list); + for (c = 0; c < count; c++) { + talloc_free(reply[c]); + } + assert_int_equal(ret, EOK); + d = 0; + for (c = 0; c < count; c++) { + if (strcmp(r[c].range_type, "unsupported-type") == 0) { + continue; + } + assert_string_equal(r[c].name, range_list[d]->name); + assert_string_equal(r[c].trusted_dom_sid, + range_list[d]->trusted_dom_sid); + assert_string_equal(r[c].range_type, range_list[d]->range_type); + assert_int_equal(r[c].base_id, range_list[d]->base_id); + assert_int_equal(r[c].id_range_size, range_list[d]->id_range_size); + assert_int_equal(r[c].base_rid, range_list[d]->base_rid); + assert_int_equal(r[c].secondary_base_rid, + range_list[d]->secondary_base_rid); + d++; + } + + talloc_free(range_list); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_get_idmap_data_from_range), + cmocka_unit_test_setup_teardown(test_ipa_idmap_get_ranges_from_sysdb, + setup_idmap_ctx, teardown_idmap_ctx), + cmocka_unit_test_setup_teardown(test_ipa_ranges_parse_results, + setup_idmap_ctx, teardown_idmap_ctx), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c b/src/tests/cmocka/test_ipa_subdomains_server.c new file mode 100644 index 0000000..0a13fc5 --- /dev/null +++ b/src/tests/cmocka/test_ipa_subdomains_server.c @@ -0,0 +1,1005 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests: IPA subdomain server utils tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/types.h> +#include <ifaddrs.h> +#include <arpa/inet.h> + +#define TESTS_PATH "tp_" BASE_FILE_STEM + +#include "providers/ipa/ipa_subdomains.h" +#include "providers/ipa/ipa_opts.h" +#include "providers/data_provider.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" +#include "tests/cmocka/common_mock_krb5.h" +#include "tests/cmocka/common_mock_sdap.h" +#include "tests/cmocka/common_mock_be.h" + +#define DOM_REALM "DOM.MAIN" +#define HOSTNAME "ipaserver.dom.main" +#define DOM_FLAT "DOM" + +#define TEST_AUTHID "host/"HOSTNAME +#define KEYTAB_TEST_PRINC TEST_AUTHID"@"DOM_REALM +#define KEYTAB_PATH TEST_DIR"/"TESTS_PATH"/keytab_test.keytab" + +#define SUBDOM_NAME "twoway.subdom.test" +#define SUBDOM_REALM "TWOWAY.SUBDOM.TEST" +#define SUBDOM_FLAT "TWOWAY" +#define SUBDOM_SID "S-1-2-3" + +#define CHILD_NAME "child."SUBDOM_NAME +#define CHILD_REALM "CHILD."SUBDOM_REALM +#define CHILD_FLAT "CHILD" +#define CHILD_SID "S-1-2-3-4" + +#define TEST_CONF_DB "test_ipa_subdom_server.ldb" +#define TEST_DOM_NAME "ipa_subdom_server_test" +#define TEST_ID_PROVIDER "ipa" + +#define ONEWAY_KEYTAB TEST_DIR"/"TESTS_PATH"/"SUBDOM_REALM".keytab" +#define ONEWAY_PRINC DOM_FLAT"$" +#define ONEWAY_AUTHID ONEWAY_PRINC"@"SUBDOM_REALM + +static bool global_rename_called; + +#ifdef HAVE_SELINUX +/* Provide faster implementation of Kerberos function + * krb5int_labeled_[f]?open. Real functions take care also + * about SELinux context which is very expensive operation + * and cause failures due to timeout when executing with Valgrind. + * It's approximately 40 times slower with real function + */ +FILE * +krb5int_labeled_fopen(const char *path, const char *mode) +{ + return fopen(path, mode); +} + +int +krb5int_labeled_open(const char *path, int flags, mode_t mode) +{ + return open(path, flags, mode); +} +#endif /* HAVE_SELINUX */ + +krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id) +{ + return krb5_kt_resolve(context, KEYTAB_PATH, id); +} + +static void create_dummy_keytab(const char *dummy_kt) +{ + errno_t ret; + + assert_non_null(dummy_kt); + mock_keytab_with_contents(global_talloc_context, + dummy_kt, ONEWAY_AUTHID); + + ret = access(dummy_kt, R_OK); + assert_int_equal(ret, 0); +} + +static int wrap_exec(void) +{ + const char *test_kt; + const char *fail_creating_kt; + + test_kt = getenv("TEST_KT_ENV"); + if (test_kt == NULL) { + _exit(1); + } + unsetenv("TEST_KT_ENV"); + + fail_creating_kt = getenv("KT_CREATE_FAIL"); + if (fail_creating_kt != NULL) { + _exit(1); + } + + create_dummy_keytab(test_kt); + _exit(0); + + return 1; /* Should not happen */ +} + +int __wrap_execle(const char *path, const char *arg, ...) +{ + return wrap_exec(); +} + +int __wrap_execve(const char *path, const char *arg, ...) +{ + return wrap_exec(); +} + +errno_t __real_sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl); + +errno_t __wrap_sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl) +{ + int ret; + int sret; + + ret = __real_sss_unique_filename(owner, path_tmpl); + if (ret == EOK) { + + sret = setenv("TEST_KT_ENV", path_tmpl, 1); + assert_int_equal(sret, 0); + } + return ret; +} + +int __real_rename(const char *old, const char *new); + +int __wrap_rename(const char *old, const char *new) +{ + global_rename_called = true; + return __real_rename(old, new); +} + +struct trust_test_ctx { + struct sss_test_ctx *tctx; + struct be_ctx *be_ctx; + + struct ipa_id_ctx *ipa_ctx; + bool expect_rename; +}; + +static struct ipa_id_ctx *mock_ipa_ctx(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sss_test_ctx *tctx, + const char *krb5_realm, + const char *hostname) +{ + struct ipa_id_ctx *ipa_ctx; + errno_t ret; + + ipa_ctx = talloc_zero(mem_ctx, struct ipa_id_ctx); + assert_non_null(ipa_ctx); + + ipa_ctx->ipa_options = talloc_zero(ipa_ctx, struct ipa_options); + assert_non_null(ipa_ctx->ipa_options); + + ipa_ctx->ipa_options->id = talloc_zero(ipa_ctx->ipa_options, + struct sdap_options); + assert_non_null(ipa_ctx->ipa_options->id); + + ret = sdap_copy_map(ipa_ctx->ipa_options->id, + ipa_user_map, + SDAP_OPTS_USER, + &ipa_ctx->ipa_options->id->user_map); + assert_int_equal(ret, ERR_OK); + + ret = dp_get_options(ipa_ctx->ipa_options->id, + tctx->confdb, + tctx->conf_dom_path, + ipa_def_ldap_opts, + SDAP_OPTS_BASIC, + &ipa_ctx->ipa_options->id->basic); + assert_int_equal(ret, EOK); + + ret = dp_get_options(ipa_ctx->ipa_options->basic, + tctx->confdb, + tctx->conf_dom_path, + ipa_basic_opts, + IPA_OPTS_BASIC, + &ipa_ctx->ipa_options->basic); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_string(ipa_ctx->ipa_options->basic, + IPA_KRB5_REALM, krb5_realm); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_string(ipa_ctx->ipa_options->basic, + IPA_HOSTNAME, hostname); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_bool(ipa_ctx->ipa_options->basic, + IPA_SERVER_MODE, true); + assert_int_equal(ret, EOK); + + ipa_ctx->sdap_id_ctx = mock_sdap_id_ctx(ipa_ctx, be_ctx, + ipa_ctx->ipa_options->id); + assert_non_null(ipa_ctx->sdap_id_ctx); + + return ipa_ctx; +} + +static struct ipa_server_mode_ctx *mock_server_mode(TALLOC_CTX *mem_ctx) +{ + struct ipa_server_mode_ctx *server_mode; + + server_mode = talloc_zero(mem_ctx, struct ipa_server_mode_ctx); + assert_non_null(server_mode); + + server_mode->hostname = HOSTNAME; + server_mode->realm = DOM_REALM; + + return server_mode; +} + +static void add_test_subdomains(struct trust_test_ctx *test_ctx, + uint32_t direction) +{ + errno_t + + /* Add two subdomains */ + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + SUBDOM_NAME, SUBDOM_REALM, + NULL, SUBDOM_NAME, SUBDOM_SID, + MPG_ENABLED, false, SUBDOM_REALM, + direction, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + CHILD_NAME, CHILD_REALM, + CHILD_FLAT, CHILD_NAME, CHILD_SID, + MPG_ENABLED, false, SUBDOM_REALM, + direction, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + +} + +static void add_test_2way_subdomains(struct trust_test_ctx *test_ctx) +{ + return add_test_subdomains(test_ctx, 0x1 | 0x2); +} + +static void add_test_1way_subdomains(struct trust_test_ctx *test_ctx) +{ + return add_test_subdomains(test_ctx, 0x1); +} + +static int test_ipa_server_create_trusts_setup(void **state) +{ + errno_t ret; + struct trust_test_ctx *test_ctx; + struct sss_test_conf_param params[] = { + { NULL, NULL }, /* Sentinel */ + }; + + test_ctx = talloc_zero(NULL, + struct trust_test_ctx); + assert_non_null(test_ctx); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, params); + assert_non_null(test_ctx->tctx); + test_ctx->tctx->dom->flat_name = discard_const(DOM_FLAT); + test_ctx->tctx->dom->realm = discard_const(DOM_REALM); + + test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx); + assert_non_null(test_ctx->be_ctx); + + test_ctx->ipa_ctx = mock_ipa_ctx(test_ctx, test_ctx->be_ctx, test_ctx->tctx, + DOM_REALM, HOSTNAME); + assert_non_null(test_ctx->tctx); + + test_ctx->ipa_ctx->server_mode = mock_server_mode(test_ctx->ipa_ctx); + assert_non_null(test_ctx->ipa_ctx->server_mode); + + ret = be_init_failover(test_ctx->be_ctx); + assert_int_equal(ret, EOK); + + mock_keytab_with_contents(test_ctx, KEYTAB_PATH, KEYTAB_TEST_PRINC); + + global_rename_called = false; + + *state = test_ctx; + return 0; +} + +static int test_ipa_server_create_trusts_teardown(void **state) +{ + struct trust_test_ctx *test_ctx = + talloc_get_type(*state, struct trust_test_ctx); + errno_t ret; + + ret = unlink(KEYTAB_PATH); + assert_int_equal(ret, 0); + + unlink(ONEWAY_KEYTAB); + /* Ignore failures */ + + /* If a test needs this variable, it should be set again in + * each test + */ + unsetenv("KT_CREATE_FAIL"); + + talloc_free(test_ctx); + return 0; +} + +static void test_ipa_server_create_trusts_none(struct tevent_req *req); +static void test_ipa_server_create_trusts_twoway(struct tevent_req *req); + +static void test_ipa_server_create_trusts(void **state) +{ + struct trust_test_ctx *test_ctx = + talloc_get_type(*state, struct trust_test_ctx); + struct tevent_req *req; + errno_t ret; + + req = ipa_server_create_trusts_send(test_ctx, + test_ctx->tctx->ev, + test_ctx->be_ctx, + test_ctx->ipa_ctx, + test_ctx->be_ctx->domain); + assert_non_null(req); + + tevent_req_set_callback(req, test_ipa_server_create_trusts_none, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); +} + +static void test_ipa_server_create_trusts_none(struct tevent_req *req) +{ + struct trust_test_ctx *test_ctx = \ + tevent_req_callback_data(req, struct trust_test_ctx); + errno_t ret; + + ret = ipa_server_create_trusts_recv(req); + talloc_zfree(req); + assert_int_equal(ret, EOK); + + /* Add two subdomains */ + add_test_2way_subdomains(test_ctx); + + req = ipa_server_create_trusts_send(test_ctx, + test_ctx->tctx->ev, + test_ctx->be_ctx, + test_ctx->ipa_ctx, + test_ctx->be_ctx->domain); + assert_non_null(req); + + tevent_req_set_callback(req, test_ipa_server_create_trusts_twoway, test_ctx); + +} + +static void assert_trust_object(struct ipa_ad_server_ctx *trust, + const char *dom_name, + const char *dom_realm, + const char *sid, + const char *keytab, + const char *authid, + const char *sdap_realm) +{ + const char *s; + + assert_non_null(trust); + assert_non_null(trust->dom); + assert_string_equal(trust->dom->name, dom_name); + assert_string_equal(trust->dom->domain_id, sid); + + s = dp_opt_get_string(trust->ad_id_ctx->ad_options->basic, + AD_KRB5_REALM); + if (dom_realm != NULL) { + assert_non_null(s); + assert_string_equal(s, dom_realm); + } else { + assert_null(s); + } + + s = dp_opt_get_string(trust->ad_id_ctx->ad_options->basic, + AD_DOMAIN); + if (dom_name != NULL) { + assert_non_null(s); + assert_string_equal(s, dom_name); + } else { + assert_null(s); + } + + /* both one-way and two-way trust uses specialized keytab */ + s = dp_opt_get_string(trust->ad_id_ctx->ad_options->id->basic, + SDAP_KRB5_KEYTAB); + if (keytab != NULL) { + assert_non_null(s); + assert_string_equal(s, keytab); + } else { + assert_null(s); + } + + s = dp_opt_get_string(trust->ad_id_ctx->ad_options->id->basic, + SDAP_SASL_REALM); + if (sdap_realm != NULL) { + assert_non_null(s); + assert_string_equal(s, sdap_realm); + } else { + assert_null(s); + } + + s = dp_opt_get_string(trust->ad_id_ctx->ad_options->id->basic, + SDAP_SASL_AUTHID); + if (authid != NULL) { + assert_non_null(s); + assert_string_equal(s, authid); + } else { + assert_null(s); + } +} + +static void test_ipa_server_create_trusts_twoway(struct tevent_req *req) +{ + struct trust_test_ctx *test_ctx = \ + tevent_req_callback_data(req, struct trust_test_ctx); + errno_t ret; + struct sss_domain_info *child_dom; + struct ipa_ad_server_ctx *s_trust; + struct ipa_ad_server_ctx *c_trust; + + ret = ipa_server_create_trusts_recv(req); + talloc_zfree(req); + assert_int_equal(ret, EOK); + + /* Trust object should be around now */ + assert_non_null(test_ctx->ipa_ctx->server_mode->trusts); + assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next); + + if (strcmp(test_ctx->ipa_ctx->server_mode->trusts->dom->name, + SUBDOM_NAME) == 0) { + s_trust = test_ctx->ipa_ctx->server_mode->trusts; + c_trust = test_ctx->ipa_ctx->server_mode->trusts->next; + } else { + s_trust = test_ctx->ipa_ctx->server_mode->trusts->next; + c_trust = test_ctx->ipa_ctx->server_mode->trusts; + } + assert_trust_object(c_trust, + CHILD_NAME, + CHILD_REALM, + CHILD_SID, + ONEWAY_KEYTAB, + ONEWAY_PRINC, + SUBDOM_REALM); + + + assert_trust_object(s_trust, + SUBDOM_NAME, + SUBDOM_REALM, + SUBDOM_SID, + ONEWAY_KEYTAB, + ONEWAY_PRINC, + SUBDOM_REALM); + + /* No more trust objects */ + assert_null(test_ctx->ipa_ctx->server_mode->trusts->next->next); + + ret = sysdb_subdomain_delete(test_ctx->tctx->sysdb, CHILD_NAME); + assert_int_equal(ret, EOK); + + child_dom = find_domain_by_name(test_ctx->be_ctx->domain, CHILD_NAME, true); + assert_non_null(child_dom); + + ipa_ad_subdom_remove(test_ctx->be_ctx, test_ctx->ipa_ctx, child_dom); + + assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts, + SUBDOM_NAME, + SUBDOM_REALM, + SUBDOM_SID, + ONEWAY_KEYTAB, + ONEWAY_PRINC, + SUBDOM_REALM); + assert_null(test_ctx->ipa_ctx->server_mode->trusts->next); + + test_ev_done(test_ctx->tctx, EOK); +} + +static void +ipa_server_init_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + struct trust_test_ctx *test_ctx = + talloc_get_type(pvt, struct trust_test_ctx); + + test_ctx->tctx->done = true; +} + +static void test_ipa_server_trust_init(void **state) +{ + struct trust_test_ctx *test_ctx = + talloc_get_type(*state, struct trust_test_ctx); + errno_t ret; + struct tevent_timer *timeout_handler; + struct timeval tv; + struct ipa_ad_server_ctx *s_trust; + struct ipa_ad_server_ctx *c_trust; + + add_test_2way_subdomains(test_ctx); + + ret = ipa_ad_subdom_init(test_ctx->be_ctx, test_ctx->ipa_ctx); + assert_int_equal(ret, EOK); + + tv = tevent_timeval_current_ofs(1, 0); + timeout_handler = tevent_add_timer(test_ctx->tctx->ev, test_ctx, tv, + ipa_server_init_done, test_ctx); + assert_non_null(timeout_handler); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + + /* Trust object should be around now */ + assert_non_null(test_ctx->ipa_ctx->server_mode->trusts); + assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next); + + if (strcmp(test_ctx->ipa_ctx->server_mode->trusts->dom->name, + SUBDOM_NAME) == 0) { + s_trust = test_ctx->ipa_ctx->server_mode->trusts; + c_trust = test_ctx->ipa_ctx->server_mode->trusts->next; + } else { + s_trust = test_ctx->ipa_ctx->server_mode->trusts->next; + c_trust = test_ctx->ipa_ctx->server_mode->trusts; + } + + assert_trust_object(c_trust, + CHILD_NAME, + CHILD_REALM, + CHILD_SID, + ONEWAY_KEYTAB, + ONEWAY_PRINC, + SUBDOM_REALM); + + assert_trust_object(s_trust, + SUBDOM_NAME, + SUBDOM_REALM, + SUBDOM_SID, + ONEWAY_KEYTAB, + ONEWAY_PRINC, + SUBDOM_REALM); + + /* No more trust objects */ + assert_null(test_ctx->ipa_ctx->server_mode->trusts->next->next); +} + +struct dir_test_ctx { + struct ldb_context *ldb; + struct sysdb_attrs *tdo; +}; + +static int test_get_trust_direction_setup(void **state) +{ + struct dir_test_ctx *test_ctx; + + test_ctx = talloc_zero(global_talloc_context, + struct dir_test_ctx); + assert_non_null(test_ctx); + + test_ctx->ldb = ldb_init(test_ctx, NULL); + assert_non_null(test_ctx->ldb); + + test_ctx->tdo = sysdb_new_attrs(test_ctx); + assert_non_null(test_ctx->tdo); + + *state = test_ctx; + return 0; +} + +static int test_get_trust_direction_teardown(void **state) +{ + struct dir_test_ctx *test_ctx = + talloc_get_type(*state, struct dir_test_ctx); + + talloc_free(test_ctx); + return 0; +} + +/* These are stupid tests, but test real data */ +static void test_trust_dir_getset(struct dir_test_ctx *test_ctx, + uint32_t dir_in) +{ + errno_t ret; + uint32_t dir; + + ret = sysdb_attrs_add_uint32(test_ctx->tdo, IPA_TRUST_DIRECTION, dir_in); + assert_int_equal(ret, EOK); + + ret = ipa_server_get_trust_direction(test_ctx->tdo, test_ctx->ldb, &dir); + assert_int_equal(ret, EOK); + assert_int_equal(dir, dir_in); +} + +static void test_get_trust_direction_inbound(void **state) +{ + struct dir_test_ctx *test_ctx = + talloc_get_type(*state, struct dir_test_ctx); + + test_trust_dir_getset(test_ctx, 0x1); +} + +static void test_get_trust_direction_outbound(void **state) +{ + struct dir_test_ctx *test_ctx = + talloc_get_type(*state, struct dir_test_ctx); + + test_trust_dir_getset(test_ctx, 0x2); +} + +static void test_get_trust_direction_twoway(void **state) +{ + struct dir_test_ctx *test_ctx = + talloc_get_type(*state, struct dir_test_ctx); + + test_trust_dir_getset(test_ctx, 0x1 | 0x2); +} + +static void test_get_trust_direction_notset_root(void **state) +{ + errno_t ret; + uint32_t dir; + struct dir_test_ctx *test_ctx = + talloc_get_type(*state, struct dir_test_ctx); + + ret = sysdb_attrs_add_string(test_ctx->tdo, SYSDB_ORIG_DN, + "cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com"); + assert_int_equal(ret, EOK); + + ret = ipa_server_get_trust_direction(test_ctx->tdo, test_ctx->ldb, &dir); + assert_int_equal(ret, EOK); + + /* With root domains we assume two-way trust */ + assert_int_equal(dir, 0x1 | 0x2); +} + +static void test_get_trust_direction_notset_member(void **state) +{ + errno_t ret; + uint32_t dir; + struct dir_test_ctx *test_ctx = + talloc_get_type(*state, struct dir_test_ctx); + + ret = sysdb_attrs_add_string(test_ctx->tdo, SYSDB_ORIG_DN, + "cn=SUB.AD.DOM,cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com"); + assert_int_equal(ret, EOK); + + ret = ipa_server_get_trust_direction(test_ctx->tdo, test_ctx->ldb, &dir); + assert_int_equal(ret, EOK); + + /* With members we set zero and take a look at the parent */ + assert_int_equal(dir, 0); +} + +static void test_ipa_server_create_trusts_oneway(struct tevent_req *req); + +static void test_ipa_server_create_oneway(void **state) +{ + struct trust_test_ctx *test_ctx = + talloc_get_type(*state, struct trust_test_ctx); + struct tevent_req *req; + errno_t ret; + + add_test_1way_subdomains(test_ctx); + + ret = access(ONEWAY_KEYTAB, R_OK); + assert_int_not_equal(ret, 0); + + assert_null(test_ctx->ipa_ctx->server_mode->trusts); + + test_ctx->expect_rename = true; + + req = ipa_server_create_trusts_send(test_ctx, + test_ctx->tctx->ev, + test_ctx->be_ctx, + test_ctx->ipa_ctx, + test_ctx->be_ctx->domain); + assert_non_null(req); + + tevent_req_set_callback(req, test_ipa_server_create_trusts_oneway, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); +} + +static void test_ipa_server_create_trusts_oneway(struct tevent_req *req) +{ + struct trust_test_ctx *test_ctx = \ + tevent_req_callback_data(req, struct trust_test_ctx); + errno_t ret; + struct ipa_ad_server_ctx *s_trust; + struct ipa_ad_server_ctx *c_trust; + + ret = ipa_server_create_trusts_recv(req); + talloc_zfree(req); + assert_int_equal(ret, EOK); + + assert_true(test_ctx->expect_rename == global_rename_called); + + ret = access(ONEWAY_KEYTAB, R_OK); + assert_int_equal(ret, 0); + + /* Trust object should be around now */ + assert_non_null(test_ctx->ipa_ctx->server_mode->trusts); + assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next); + + if (strcmp(test_ctx->ipa_ctx->server_mode->trusts->dom->name, + SUBDOM_NAME) == 0) { + s_trust = test_ctx->ipa_ctx->server_mode->trusts; + c_trust = test_ctx->ipa_ctx->server_mode->trusts->next; + } else { + s_trust = test_ctx->ipa_ctx->server_mode->trusts->next; + c_trust = test_ctx->ipa_ctx->server_mode->trusts; + } + + assert_trust_object( + c_trust, + CHILD_NAME, /* AD domain name */ + CHILD_REALM, /* AD realm can be child if SDAP realm is parent's */ + CHILD_SID, + ONEWAY_KEYTAB, /* Keytab shared with parent AD dom */ + ONEWAY_PRINC, /* Principal shared with parent AD dom */ + SUBDOM_REALM); /* SDAP realm must be AD root domain */ + + /* Here all properties point to the AD domain */ + assert_trust_object(s_trust, + SUBDOM_NAME, + SUBDOM_REALM, + SUBDOM_SID, + ONEWAY_KEYTAB, + ONEWAY_PRINC, + SUBDOM_REALM); + + assert_null(test_ctx->ipa_ctx->server_mode->trusts->next->next); + test_ev_done(test_ctx->tctx, EOK); +} + +static void test_ipa_server_create_oneway_kt_exists(void **state) +{ + struct trust_test_ctx *test_ctx = + talloc_get_type(*state, struct trust_test_ctx); + struct tevent_req *req; + errno_t ret; + + add_test_1way_subdomains(test_ctx); + + create_dummy_keytab(ONEWAY_KEYTAB); + ret = access(ONEWAY_KEYTAB, R_OK); + assert_int_equal(ret, 0); + + test_ctx->expect_rename = true; + + assert_null(test_ctx->ipa_ctx->server_mode->trusts); + + req = ipa_server_create_trusts_send(test_ctx, + test_ctx->tctx->ev, + test_ctx->be_ctx, + test_ctx->ipa_ctx, + test_ctx->be_ctx->domain); + assert_non_null(req); + + tevent_req_set_callback(req, test_ipa_server_create_trusts_oneway, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); +} + +/* Test scenario where a keytab already exists, but refresh fails. In this case, + * sssd should attempt to reuse the previous keytab + */ +static void test_ipa_server_create_oneway_kt_refresh_fallback(void **state) +{ + struct trust_test_ctx *test_ctx = + talloc_get_type(*state, struct trust_test_ctx); + struct tevent_req *req; + errno_t ret; + + add_test_1way_subdomains(test_ctx); + + create_dummy_keytab(ONEWAY_KEYTAB); + ret = access(ONEWAY_KEYTAB, R_OK); + assert_int_equal(ret, 0); + + setenv("KT_CREATE_FAIL", "1", 1); + test_ctx->expect_rename = false; + + assert_null(test_ctx->ipa_ctx->server_mode->trusts); + + req = ipa_server_create_trusts_send(test_ctx, + test_ctx->tctx->ev, + test_ctx->be_ctx, + test_ctx->ipa_ctx, + test_ctx->be_ctx->domain); + assert_non_null(req); + + tevent_req_set_callback(req, test_ipa_server_create_trusts_oneway, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); +} + +/* Tests case where there's no keytab and retrieving fails. Just fail the + * request in that case + */ +static void test_ipa_server_create_trusts_oneway_fail(struct tevent_req *req); + +static void test_ipa_server_create_oneway_kt_refresh_fail(void **state) +{ + struct trust_test_ctx *test_ctx = + talloc_get_type(*state, struct trust_test_ctx); + struct tevent_req *req; + errno_t ret; + + add_test_1way_subdomains(test_ctx); + + setenv("KT_CREATE_FAIL", "1", 1); + test_ctx->expect_rename = false; + + assert_null(test_ctx->ipa_ctx->server_mode->trusts); + + req = ipa_server_create_trusts_send(test_ctx, + test_ctx->tctx->ev, + test_ctx->be_ctx, + test_ctx->ipa_ctx, + test_ctx->be_ctx->domain); + assert_non_null(req); + + tevent_req_set_callback(req, + test_ipa_server_create_trusts_oneway_fail, + test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); +} + +static void test_ipa_server_create_trusts_oneway_fail(struct tevent_req *req) +{ + struct trust_test_ctx *test_ctx = \ + tevent_req_callback_data(req, struct trust_test_ctx); + errno_t ret; + + ret = ipa_server_create_trusts_recv(req); + assert_int_not_equal(ret, EOK); + + assert_true(test_ctx->expect_rename == global_rename_called); + + test_ev_done(test_ctx->tctx, EOK); +} + +static void test_ipa_server_trust_oneway_init(void **state) +{ + struct trust_test_ctx *test_ctx = + talloc_get_type(*state, struct trust_test_ctx); + errno_t ret; + struct tevent_timer *timeout_handler; + struct timeval tv; + + add_test_1way_subdomains(test_ctx); + + ret = ipa_ad_subdom_init(test_ctx->be_ctx, test_ctx->ipa_ctx); + assert_int_equal(ret, EOK); + + tv = tevent_timeval_current_ofs(1, 0); + timeout_handler = tevent_add_timer(test_ctx->tctx->ev, test_ctx, tv, + ipa_server_init_done, test_ctx); + assert_non_null(timeout_handler); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + + assert_non_null(test_ctx->ipa_ctx->server_mode->trusts); +} + +static void test_ipa_trust_dir2str(void **state) +{ + /* Just make sure the caller can rely on getting a valid string.. */ + assert_non_null(ipa_trust_dir2str(0x00)); + assert_non_null(ipa_trust_dir2str(0x01)); + assert_non_null(ipa_trust_dir2str(0x02)); + assert_non_null(ipa_trust_dir2str(0x80)); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + { "no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_ipa_trust_dir2str), + + cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway, + test_ipa_server_create_trusts_setup, + test_ipa_server_create_trusts_teardown), + cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_exists, + test_ipa_server_create_trusts_setup, + test_ipa_server_create_trusts_teardown), + cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_refresh_fallback, + test_ipa_server_create_trusts_setup, + test_ipa_server_create_trusts_teardown), + cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_refresh_fail, + test_ipa_server_create_trusts_setup, + test_ipa_server_create_trusts_teardown), + cmocka_unit_test_setup_teardown(test_ipa_server_trust_oneway_init, + test_ipa_server_create_trusts_setup, + test_ipa_server_create_trusts_teardown), + + cmocka_unit_test_setup_teardown(test_ipa_server_trust_init, + test_ipa_server_create_trusts_setup, + test_ipa_server_create_trusts_teardown), + cmocka_unit_test_setup_teardown(test_ipa_server_create_trusts, + test_ipa_server_create_trusts_setup, + test_ipa_server_create_trusts_teardown), + + cmocka_unit_test_setup_teardown(test_get_trust_direction_inbound, + test_get_trust_direction_setup, + test_get_trust_direction_teardown), + cmocka_unit_test_setup_teardown(test_get_trust_direction_outbound, + test_get_trust_direction_setup, + test_get_trust_direction_teardown), + cmocka_unit_test_setup_teardown(test_get_trust_direction_twoway, + test_get_trust_direction_setup, + test_get_trust_direction_teardown), + cmocka_unit_test_setup_teardown(test_get_trust_direction_notset_root, + test_get_trust_direction_setup, + test_get_trust_direction_teardown), + cmocka_unit_test_setup_teardown(test_get_trust_direction_notset_member, + test_get_trust_direction_setup, + test_get_trust_direction_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + + return rv; +} diff --git a/src/tests/cmocka/test_ipa_subdomains_utils.c b/src/tests/cmocka/test_ipa_subdomains_utils.c new file mode 100644 index 0000000..59cdafa --- /dev/null +++ b/src/tests/cmocka/test_ipa_subdomains_utils.c @@ -0,0 +1,227 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests: IPA subdomain util tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "providers/ipa/ipa_subdomains.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" + +struct test_ipa_subdom_ctx { + struct ldb_context *ldb; +}; + +static int test_ipa_subdom_setup(void **state) +{ + struct test_ipa_subdom_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ipa_subdom_ctx); + assert_non_null(test_ctx); + + test_ctx->ldb = ldb_init(test_ctx, NULL); + assert_non_null(test_ctx->ldb); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int test_ipa_subdom_teardown(void **state) +{ + struct test_ipa_subdom_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct test_ipa_subdom_ctx); + assert_non_null(test_ctx); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static struct sysdb_attrs *dn_attrs(TALLOC_CTX *mem_ctx, const char *dn) +{ + struct sysdb_attrs *attrs; + int rv; + + attrs = sysdb_new_attrs(mem_ctx); + assert_non_null(attrs); + + rv = sysdb_attrs_add_string(attrs, SYSDB_ORIG_DN, dn); + assert_int_equal(rv, EOK); + + return attrs; +} + +static void test_ipa_subdom_ldb_dn(void **state) +{ + struct ldb_dn *dn; + struct sysdb_attrs *attrs; + struct test_ipa_subdom_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct test_ipa_subdom_ctx); + assert_non_null(test_ctx); + + attrs = dn_attrs(test_ctx, "dc=foo,dc=bar"); + assert_non_null(attrs); + + dn = ipa_subdom_ldb_dn(test_ctx, test_ctx->ldb, attrs); + assert_non_null(dn); + assert_string_equal(ldb_dn_get_linearized(dn), "dc=foo,dc=bar"); + + talloc_free(dn); + talloc_free(attrs); +} + +static void test_ipa_subdom_ldb_dn_fail(void **state) +{ + struct ldb_dn *dn; + struct sysdb_attrs *attrs; + struct test_ipa_subdom_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct test_ipa_subdom_ctx); + assert_non_null(test_ctx); + + attrs = dn_attrs(test_ctx, "notadn"); + assert_non_null(attrs); + + dn = ipa_subdom_ldb_dn(test_ctx, NULL, NULL); + assert_null(dn); + + dn = ipa_subdom_ldb_dn(test_ctx, test_ctx->ldb, attrs); + assert_null(dn); + talloc_free(attrs); + + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + dn = ipa_subdom_ldb_dn(test_ctx, test_ctx->ldb, attrs); + assert_null(dn); + talloc_free(attrs); +} + +static struct ldb_dn *get_dn(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + const char *strdn) +{ + struct ldb_dn *dn; + struct sysdb_attrs *attrs; + + attrs = dn_attrs(mem_ctx, strdn); + assert_non_null(attrs); + + dn = ipa_subdom_ldb_dn(mem_ctx, ldb, attrs); + talloc_free(attrs); + assert_non_null(dn); + + return dn; +} + +static void test_ipa_subdom_is_member_dom(void **state) +{ + struct ldb_dn *dn; + struct test_ipa_subdom_ctx *test_ctx; + bool is_member; + + test_ctx = talloc_get_type(*state, struct test_ipa_subdom_ctx); + + dn = get_dn(test_ctx, test_ctx->ldb, + "cn=SUB.AD.DOM,cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com"); + is_member = ipa_subdom_is_member_dom(dn); + talloc_free(dn); + assert_true(is_member); + + dn = get_dn(test_ctx, test_ctx->ldb, + "cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com"); + is_member = ipa_subdom_is_member_dom(dn); + talloc_free(dn); + assert_false(is_member); + + dn = get_dn(test_ctx, test_ctx->ldb, + "cn=SUB.AD.DOM,cn=AD.DOM,cn=ad,cn=XXX,dc=example,dc=com"); + is_member = ipa_subdom_is_member_dom(dn); + talloc_free(dn); + assert_false(is_member); + + dn = get_dn(test_ctx, test_ctx->ldb, + "cn=SUB.AD.DOM,cn=AD.DOM,cn=YYY,cn=trusts,dc=example,dc=com"); + is_member = ipa_subdom_is_member_dom(dn); + talloc_free(dn); + assert_false(is_member); +} + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_ipa_subdom_ldb_dn, + test_ipa_subdom_setup, + test_ipa_subdom_teardown), + cmocka_unit_test_setup_teardown(test_ipa_subdom_ldb_dn_fail, + test_ipa_subdom_setup, + test_ipa_subdom_teardown), + cmocka_unit_test_setup_teardown(test_ipa_subdom_is_member_dom, + test_ipa_subdom_setup, + test_ipa_subdom_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + return rv; +} diff --git a/src/tests/cmocka/test_kcm_marshalling.c b/src/tests/cmocka/test_kcm_marshalling.c new file mode 100644 index 0000000..8e8b386 --- /dev/null +++ b/src/tests/cmocka/test_kcm_marshalling.c @@ -0,0 +1,363 @@ +/* + Copyright (C) 2017 Red Hat + + SSSD tests: Test KCM JSON marshalling + + 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 "config.h" + +#include <stdio.h> +#include <popt.h> + +#include "util/util_creds.h" +#include "responder/kcm/kcmsrv_ccache.h" +#include "responder/kcm/kcmsrv_ccache_be.h" +#include "tests/cmocka/common_mock.h" + +#define TEST_REALM "TESTREALM" +#define TEST_PRINC_COMPONENT "PRINC_NAME" + +#define TEST_CREDS "TESTCREDS" + +#define TEST_UUID_STR "5f8f296b-02be-4e86-9235-500e82354186" +#define TEST_SEC_KEY_ONEDIGIT TEST_UUID_STR"-0" +#define TEST_SEC_KEY_MULTIDIGITS TEST_UUID_STR"-123456" + +#define TEST_SEC_KEY_NOSEP TEST_UUID_STR"+0" + +const struct kcm_ccdb_ops ccdb_mem_ops; +const struct kcm_ccdb_ops ccdb_secdb_ops; + +struct kcm_marshalling_test_ctx { + krb5_context kctx; + krb5_principal princ; +}; + +static int setup_kcm_marshalling(void **state) +{ + struct kcm_marshalling_test_ctx *test_ctx; + krb5_error_code kerr; + + test_ctx = talloc_zero(NULL, struct kcm_marshalling_test_ctx); + assert_non_null(test_ctx); + + kerr = krb5_init_context(&test_ctx->kctx); + assert_int_equal(kerr, 0); + + kerr = krb5_build_principal(test_ctx->kctx, + &test_ctx->princ, + sizeof(TEST_REALM)-1, TEST_REALM, + TEST_PRINC_COMPONENT, NULL); + assert_int_equal(kerr, 0); + + *state = test_ctx; + return 0; +} + +static int teardown_kcm_marshalling(void **state) +{ + struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state, + struct kcm_marshalling_test_ctx); + assert_non_null(test_ctx); + + krb5_free_principal(test_ctx->kctx, test_ctx->princ); + krb5_free_context(test_ctx->kctx); + talloc_free(test_ctx); + return 0; +} + +static void assert_cc_name_equal(struct kcm_ccache *cc1, + struct kcm_ccache *cc2) +{ + const char *name1, *name2; + + name1 = kcm_cc_get_name(cc1); + name2 = kcm_cc_get_name(cc2); + assert_string_equal(name1, name2); +} + +static void assert_cc_uuid_equal(struct kcm_ccache *cc1, + struct kcm_ccache *cc2) +{ + uuid_t u1, u2; + errno_t ret; + + ret = kcm_cc_get_uuid(cc1, u1); + assert_int_equal(ret, EOK); + ret = kcm_cc_get_uuid(cc2, u2); + assert_int_equal(ret, EOK); + ret = uuid_compare(u1, u2); + assert_int_equal(ret, 0); +} + +static void assert_cc_princ_equal(struct kcm_ccache *cc1, + struct kcm_ccache *cc2) +{ + krb5_principal p1; + krb5_principal p2; + char *name1; + char *name2; + krb5_error_code kerr; + + p1 = kcm_cc_get_client_principal(cc1); + p2 = kcm_cc_get_client_principal(cc2); + + if (p1 != NULL && p2 != NULL) { + kerr = krb5_unparse_name(NULL, p1, &name1); + assert_int_equal(kerr, 0); + kerr = krb5_unparse_name(NULL, p2, &name2); + assert_int_equal(kerr, 0); + + assert_string_equal(name1, name2); + krb5_free_unparsed_name(NULL, name1); + krb5_free_unparsed_name(NULL, name2); + } else { + /* Either both principals must be NULL or both + * non-NULL and represent the same principals + */ + assert_null(p1); + assert_null(p2); + } +} + +static void assert_cc_offset_equal(struct kcm_ccache *cc1, + struct kcm_ccache *cc2) +{ + int32_t off1; + int32_t off2; + + off1 = kcm_cc_get_offset(cc1); + off2 = kcm_cc_get_offset(cc2); + assert_int_equal(off1, off2); +} + +static void assert_cc_equal(struct kcm_ccache *cc1, + struct kcm_ccache *cc2) +{ + assert_cc_name_equal(cc1, cc2); + assert_cc_uuid_equal(cc1, cc2); + assert_cc_princ_equal(cc1, cc2); + assert_cc_offset_equal(cc1, cc2); +} + +static void test_kcm_ccache_marshall_unmarshall_binary(void **state) +{ + struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state, + struct kcm_marshalling_test_ctx); + errno_t ret; + struct cli_creds owner; + struct kcm_ccache *cc; + struct kcm_ccache *cc2; + struct sss_iobuf *payload; + const char *name; + const char *key; + uint8_t *data; + uuid_t uuid; + + owner.ucred.uid = getuid(); + owner.ucred.gid = getuid(); + + name = talloc_asprintf(test_ctx, "%"SPRIuid, getuid()); + assert_non_null(name); + + ret = kcm_cc_new(test_ctx, + test_ctx->kctx, + &owner, + name, + test_ctx->princ, + &cc); + assert_int_equal(ret, EOK); + + ret = kcm_ccache_to_sec_input_binary(test_ctx, cc, &payload); + assert_int_equal(ret, EOK); + + data = sss_iobuf_get_data(payload); + assert_non_null(data); + + ret = kcm_cc_get_uuid(cc, uuid); + assert_int_equal(ret, EOK); + key = sec_key_create(test_ctx, name, uuid); + assert_non_null(key); + + sss_iobuf_cursor_reset(payload); + ret = sec_kv_to_ccache_binary(test_ctx, key, payload, &owner, &cc2); + assert_int_equal(ret, EOK); + + assert_cc_equal(cc, cc2); + + /* This key is exactly one byte shorter than it should be */ + sss_iobuf_cursor_reset(payload); + ret = sec_kv_to_ccache_binary(test_ctx, TEST_UUID_STR "-", payload, &owner, + &cc2); + assert_int_equal(ret, EINVAL); +} + +static void test_kcm_ccache_no_princ_binary(void **state) +{ + struct kcm_marshalling_test_ctx *test_ctx = talloc_get_type(*state, + struct kcm_marshalling_test_ctx); + errno_t ret; + struct cli_creds owner; + const char *name; + struct kcm_ccache *cc; + krb5_principal princ; + struct kcm_ccache *cc2; + struct sss_iobuf *payload; + const char *key; + uint8_t *data; + uuid_t uuid; + + owner.ucred.uid = getuid(); + owner.ucred.gid = getuid(); + + name = talloc_asprintf(test_ctx, "%"SPRIuid, getuid()); + assert_non_null(name); + + ret = kcm_cc_new(test_ctx, + test_ctx->kctx, + &owner, + name, + NULL, + &cc); + assert_int_equal(ret, EOK); + + princ = kcm_cc_get_client_principal(cc); + assert_null(princ); + + ret = kcm_ccache_to_sec_input_binary(test_ctx, cc, &payload); + assert_int_equal(ret, EOK); + + data = sss_iobuf_get_data(payload); + assert_non_null(data); + + ret = kcm_cc_get_uuid(cc, uuid); + assert_int_equal(ret, EOK); + key = sec_key_create(test_ctx, name, uuid); + assert_non_null(key); + + sss_iobuf_cursor_reset(payload); + ret = sec_kv_to_ccache_binary(test_ctx, key, payload, &owner, &cc2); + assert_int_equal(ret, EOK); + + assert_cc_equal(cc, cc2); +} + +void test_sec_key_get_uuid(void **state) +{ + errno_t ret; + uuid_t uuid; + char str_uuid[UUID_STR_SIZE]; + + uuid_clear(uuid); + ret = sec_key_get_uuid(TEST_SEC_KEY_ONEDIGIT, uuid); + assert_int_equal(ret, EOK); + uuid_unparse(uuid, str_uuid); + assert_string_equal(TEST_UUID_STR, str_uuid); + + ret = sec_key_get_uuid(TEST_SEC_KEY_NOSEP, uuid); + assert_int_equal(ret, EINVAL); + + ret = sec_key_get_uuid(TEST_UUID_STR, uuid); + assert_int_equal(ret, EINVAL); + + ret = sec_key_get_uuid(NULL, uuid); + assert_int_equal(ret, EINVAL); +} + +void test_sec_key_get_name(void **state) +{ + const char *name; + + name = sec_key_get_name(TEST_SEC_KEY_ONEDIGIT); + assert_non_null(name); + assert_string_equal(name, "0"); + + name = sec_key_get_name(TEST_SEC_KEY_MULTIDIGITS); + assert_non_null(name); + assert_string_equal(name, "123456"); + + name = sec_key_get_name(TEST_UUID_STR); + assert_null(name); + + name = sec_key_get_name(TEST_SEC_KEY_NOSEP); + assert_null(name); + + name = sec_key_get_name(NULL); + assert_null(name); +} + +void test_sec_key_match_name(void **state) +{ + assert_true(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, "0")); + assert_true(sec_key_match_name(TEST_SEC_KEY_MULTIDIGITS, "123456")); + + assert_false(sec_key_match_name(TEST_SEC_KEY_MULTIDIGITS, "0")); + assert_false(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, "123456")); + + assert_false(sec_key_match_name(TEST_UUID_STR, "0")); + assert_false(sec_key_match_name(TEST_SEC_KEY_NOSEP, "0")); + assert_false(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, NULL)); + assert_false(sec_key_match_name(NULL, "0")); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall_binary, + setup_kcm_marshalling, + teardown_kcm_marshalling), + cmocka_unit_test_setup_teardown(test_kcm_ccache_no_princ_binary, + setup_kcm_marshalling, + teardown_kcm_marshalling), + cmocka_unit_test(test_sec_key_get_uuid), + cmocka_unit_test(test_sec_key_get_name), + cmocka_unit_test(test_sec_key_match_name), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/cmocka/test_kcm_queue.c b/src/tests/cmocka/test_kcm_queue.c new file mode 100644 index 0000000..5a2e6e1 --- /dev/null +++ b/src/tests/cmocka/test_kcm_queue.c @@ -0,0 +1,410 @@ +/* + Copyright (C) 2017 Red Hat + + SSSD tests: Test KCM wait queue + + 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 "config.h" + +#include <stdio.h> +#include <popt.h> + +#include "util/util.h" +#include "util/util_creds.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" +#include "responder/kcm/kcmsrv_pvt.h" + +#define INVALID_ID -1 +#define FAST_REQ_ID 0 +#define SLOW_REQ_ID 1 + +#define FAST_REQ_DELAY 1 +#define SLOW_REQ_DELAY 2 + +/* register_cli_protocol_version is required in test since it links with + * responder_common.c module + */ +struct cli_protocol_version *register_cli_protocol_version(void) +{ + static struct cli_protocol_version responder_test_cli_protocol_version[] = { + { 0, NULL, NULL } + }; + + return responder_test_cli_protocol_version; +} + +struct timed_request_state { + struct tevent_context *ev; + struct resp_ctx *rctx; + struct kcm_ops_queue_ctx *qctx; + struct cli_creds *client; + int delay; + int req_id; + + struct kcm_ops_queue_entry *queue_entry; +}; + +static void timed_request_start(struct tevent_req *subreq); +static void timed_request_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt); + +static struct tevent_req *timed_request_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resp_ctx *rctx, + struct kcm_ops_queue_ctx *qctx, + struct cli_creds *client, + int delay, + int req_id) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct timed_request_state *state; + + req = tevent_req_create(mem_ctx, &state, struct timed_request_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->rctx = rctx; + state->qctx = qctx; + state->client = client; + state->delay = delay; + state->req_id = req_id; + + DEBUG(SSSDBG_TRACE_ALL, "Request %p with delay %d\n", req, delay); + + subreq = kcm_op_queue_send(state, ev, qctx, client); + if (subreq == NULL) { + return NULL; + } + tevent_req_set_callback(subreq, timed_request_start, req); + + return req; +} + +static void timed_request_start(struct tevent_req *subreq) +{ + struct timeval tv; + struct tevent_timer *timeout = NULL; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct timed_request_state *state = tevent_req_data(req, + struct timed_request_state); + errno_t ret; + + ret = kcm_op_queue_recv(subreq, state, &state->queue_entry); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tv = tevent_timeval_current_ofs(state->delay, 0); + timeout = tevent_add_timer(state->ev, state, tv, timed_request_done, req); + if (timeout == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + return; +} + +static void timed_request_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + DEBUG(SSSDBG_TRACE_ALL, "Request %p done\n", req); + tevent_req_done(req); +} + +static errno_t timed_request_recv(struct tevent_req *req, + int *req_id) +{ + struct timed_request_state *state = tevent_req_data(req, + struct timed_request_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + *req_id = state->req_id; + return EOK; +} + +struct test_ctx { + struct resp_ctx *rctx; + struct kcm_ops_queue_ctx *qctx; + struct kcm_ctx *kctx; + struct tevent_context *ev; + + int *req_ids; + + int num_requests; + int finished_requests; + bool done; + errno_t error; +}; + +struct kcm_ctx * +mock_kctx(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx) +{ + struct kcm_ctx *kctx; + + kctx = talloc_zero(mem_ctx, struct kcm_ctx); + if (!kctx) { + return NULL; + } + + kctx->rctx = rctx; + + return kctx; +} + +static int setup_kcm_queue(void **state) +{ + struct test_ctx *tctx; + + tctx = talloc_zero(NULL, struct test_ctx); + assert_non_null(tctx); + + tctx->ev = tevent_context_init(tctx); + assert_non_null(tctx->ev); + + tctx->rctx = mock_rctx(tctx, tctx->ev, NULL, NULL); + assert_non_null(tctx->rctx); + + tctx->kctx = mock_kctx(tctx, tctx->rctx); + assert_non_null(tctx->kctx); + + tctx->qctx = kcm_ops_queue_create(tctx->kctx, tctx->kctx); + assert_non_null(tctx->qctx); + + *state = tctx; + return 0; +} + +static int teardown_kcm_queue(void **state) +{ + struct test_ctx *tctx = talloc_get_type(*state, struct test_ctx); + talloc_free(tctx); + return 0; +} + +static void test_kcm_queue_done(struct tevent_req *req) +{ + struct test_ctx *test_ctx = tevent_req_callback_data(req, + struct test_ctx); + int req_id = INVALID_ID; + + test_ctx->error = timed_request_recv(req, &req_id); + talloc_zfree(req); + if (test_ctx->error != EOK) { + test_ctx->done = true; + return; + } + + if (test_ctx->req_ids[test_ctx->finished_requests] != req_id) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Request %d finished, expected %d\n", + req_id, test_ctx->req_ids[test_ctx->finished_requests]); + test_ctx->error = EIO; + test_ctx->done = true; + return; + } + + test_ctx->finished_requests++; + if (test_ctx->finished_requests == test_ctx->num_requests) { + test_ctx->done = true; + return; + } +} + +/* + * Just make sure that a single pass through the queue works + */ +static void test_kcm_queue_single(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + struct tevent_req *req; + struct cli_creds client; + static int req_ids[] = { 0 }; + + client.ucred.uid = getuid(); + client.ucred.gid = getgid(); + + req = timed_request_send(test_ctx, + test_ctx->ev, + test_ctx->rctx, + test_ctx->qctx, + &client, 1, 0); + assert_non_null(req); + tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); + + test_ctx->num_requests = 1; + test_ctx->req_ids = req_ids; + + while (test_ctx->done == false) { + tevent_loop_once(test_ctx->ev); + } + assert_int_equal(test_ctx->error, EOK); +} + +/* + * Test that multiple requests from the same ID wait for one another + */ +static void test_kcm_queue_multi_same_id(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + struct tevent_req *req; + struct cli_creds client; + /* The slow request will finish first because request from + * the same ID are serialized + */ + static int req_ids[] = { SLOW_REQ_ID, FAST_REQ_ID }; + + client.ucred.uid = getuid(); + client.ucred.gid = getgid(); + + req = timed_request_send(test_ctx, + test_ctx->ev, + test_ctx->rctx, + test_ctx->qctx, + &client, + SLOW_REQ_DELAY, + SLOW_REQ_ID); + assert_non_null(req); + tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); + + req = timed_request_send(test_ctx, + test_ctx->ev, + test_ctx->rctx, + test_ctx->qctx, + &client, + FAST_REQ_DELAY, + FAST_REQ_ID); + assert_non_null(req); + tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); + + test_ctx->num_requests = 2; + test_ctx->req_ids = req_ids; + + while (test_ctx->done == false) { + tevent_loop_once(test_ctx->ev); + } + assert_int_equal(test_ctx->error, EOK); +} + +/* + * Test that multiple requests from different IDs don't wait for one + * another and can run concurrently + */ +static void test_kcm_queue_multi_different_id(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + struct tevent_req *req; + struct cli_creds client; + /* In this test, the fast request will finish sooner because + * both requests are from different IDs, allowing them to run + * concurrently + */ + static int req_ids[] = { FAST_REQ_ID, SLOW_REQ_ID }; + + client.ucred.uid = getuid(); + client.ucred.gid = getgid(); + + req = timed_request_send(test_ctx, + test_ctx->ev, + test_ctx->rctx, + test_ctx->qctx, + &client, + SLOW_REQ_DELAY, + SLOW_REQ_ID); + assert_non_null(req); + tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); + + client.ucred.uid = getuid() + 1; + client.ucred.gid = getgid() + 1; + + req = timed_request_send(test_ctx, + test_ctx->ev, + test_ctx->rctx, + test_ctx->qctx, + &client, + FAST_REQ_DELAY, + FAST_REQ_ID); + assert_non_null(req); + tevent_req_set_callback(req, test_kcm_queue_done, test_ctx); + + test_ctx->num_requests = 2; + test_ctx->req_ids = req_ids; + + while (test_ctx->done == false) { + tevent_loop_once(test_ctx->ev); + } + assert_int_equal(test_ctx->error, EOK); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_kcm_queue_single, + setup_kcm_queue, + teardown_kcm_queue), + cmocka_unit_test_setup_teardown(test_kcm_queue_multi_same_id, + setup_kcm_queue, + teardown_kcm_queue), + cmocka_unit_test_setup_teardown(test_kcm_queue_multi_different_id, + setup_kcm_queue, + teardown_kcm_queue), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/cmocka/test_kcm_renewals.c b/src/tests/cmocka/test_kcm_renewals.c new file mode 100644 index 0000000..c8702b5 --- /dev/null +++ b/src/tests/cmocka/test_kcm_renewals.c @@ -0,0 +1,188 @@ +/* + Copyright (C) 2020 Red Hat + + SSSD tests: Test KCM Renewals + + 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 "config.h" + +#include <stdio.h> +#include <popt.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "util/util.h" +#include "util/util_creds.h" +#include "tests/cmocka/common_mock.h" +#include "responder/kcm/kcmsrv_ccache.h" +#include "responder/kcm/kcm_renew.h" +#include "responder/kcm/kcmsrv_ccache_be.h" +#include "responder/kcm/kcmsrv_ccache_pvt.h" +#include "responder/kcm/kcmsrv_pvt.h" +#include "responder/kcm/kcmsrv_ccache_secdb.c" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_kcm_renewals_conf.ldb" +#define TEST_DB_FULL_PATH TESTS_PATH "/secrets.ldb" + +errno_t sss_sec_init_with_path(TALLOC_CTX *mem_ctx, + struct sss_sec_quota *quota, + const char *dbpath, + struct sss_sec_ctx **_sec_ctx); + +errno_t kcm_renew_all_tgts(TALLOC_CTX *mem_ctx, + struct kcm_renew_tgt_ctx *renew_tgt_ctx, + struct kcm_ccache **cc_list); + +const struct kcm_ccdb_ops ccdb_mem_ops; +const struct kcm_ccdb_ops ccdb_secdb_ops; + +struct test_ctx { + struct krb5_ctx *krb5_ctx; + struct tevent_context *ev; + struct kcm_ccdb *ccdb; +}; + +/* register_cli_protocol_version is required in test since it links with + * responder_common.c module + */ +struct cli_protocol_version *register_cli_protocol_version(void) +{ + static struct cli_protocol_version responder_test_cli_protocol_version[] = { + { 0, NULL, NULL } + }; + + return responder_test_cli_protocol_version; +} + +static int setup_kcm_renewals(void **state) +{ + struct test_ctx *tctx; + + tctx = talloc_zero(NULL, struct test_ctx); + assert_non_null(tctx); + + tctx->ev = tevent_context_init(tctx); + assert_non_null(tctx->ev); + + tctx->ccdb = talloc_zero(tctx, struct kcm_ccdb); + assert_non_null(tctx->ccdb); + tctx->ccdb->ev = tctx->ev; + + tctx->ccdb->ops = &ccdb_secdb_ops; + assert_non_null(tctx->ccdb->ops); + + *state = tctx; + return 0; +} + +static int teardown_kcm_renewals(void **state) +{ + struct test_ctx *tctx = talloc_get_type(*state, struct test_ctx); + + unlink(TEST_DB_FULL_PATH); + + rmdir(TESTS_PATH); + talloc_free(tctx); + return 0; +} + +static void test_kcm_renewals_tgt(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + errno_t ret; + struct ccdb_secdb *secdb = NULL; + struct kcm_renew_tgt_ctx *renew_tgt_ctx = NULL; + struct kcm_ccache **cc_list; + struct kcm_ccache *cc; + + secdb = talloc_zero(test_ctx, struct ccdb_secdb); + + assert_non_null(secdb); + + ret = mkdir(TESTS_PATH, 0700); + assert_int_equal(ret, 0); + + open(TEST_DB_FULL_PATH, O_CREAT|O_EXCL|O_WRONLY, 0600); + + ret = sss_sec_init_with_path(test_ctx->ccdb, NULL, TEST_DB_FULL_PATH, + &secdb->sctx); + + /* Create renew ctx */ + renew_tgt_ctx = talloc_zero(test_ctx, struct kcm_renew_tgt_ctx); + renew_tgt_ctx->ev = test_ctx->ev; + + /* Create cc list */ + cc_list = talloc_zero_array(test_ctx, struct kcm_ccache *, 2); + assert_non_null(cc_list); + + cc = talloc_zero(cc_list, struct kcm_ccache); + assert_non_null(cc); + + cc->name = talloc_strdup(test_ctx, "1000:1001"); + cc->owner.uid = 1000; + cc->owner.gid = 1000; + + cc_list[0] = cc; + cc_list[1] = NULL; + + ret = kcm_renew_all_tgts(test_ctx, renew_tgt_ctx, cc_list); + assert_int_equal(ret, EOK); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_kcm_renewals_tgt, + setup_kcm_renewals, + teardown_kcm_renewals), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure + */ + tests_set_cwd(); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/cmocka/test_krb5_common.c b/src/tests/cmocka/test_krb5_common.c new file mode 100644 index 0000000..4bf3237 --- /dev/null +++ b/src/tests/cmocka/test_krb5_common.c @@ -0,0 +1,306 @@ +/* + SSSD + + krb5_common - Test for some krb5 utility functions + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2016 Red Hat + + 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> +#include <stdbool.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" + +#include "src/providers/krb5/krb5_common.h" + +#define TEST_REALM "MY.REALM" +#define TEST_FAST_PRINC "fast_princ@" TEST_REALM +#define TEST_FAST_STR "dummy" +#define TEST_LIFE_STR "dummy-life" +#define TEST_RLIFE_STR "dummy-rlife" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_krb5_common_conf.ldb" +#define TEST_DOM_NAME "test.krb5.common" +#define TEST_ID_PROVIDER "ldap" + +struct test_ctx { + struct sss_test_ctx *tctx; +}; + +static int test_setup(void **state) +{ + struct test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, + TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + check_leaks_push(test_ctx); + *state = test_ctx; + + return 0; +} + +static int test_teardown(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_true(check_leaks_pop(test_ctx)); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void test_set_extra_args(void **state) +{ + int ret; + struct krb5_ctx *krb5_ctx; + char *uid_opt; + char *gid_opt; + const char **krb5_child_extra_args; + + ret = set_extra_args(NULL, NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + krb5_ctx = talloc_zero(global_talloc_context, struct krb5_ctx); + assert_non_null(krb5_ctx); + uid_opt = talloc_asprintf(krb5_ctx, "--fast-ccache-uid=%"SPRIuid, getuid()); + assert_non_null(uid_opt); + + gid_opt = talloc_asprintf(krb5_ctx, "--fast-ccache-gid=%"SPRIgid, getgid()); + assert_non_null(gid_opt); + + ret = set_extra_args(global_talloc_context, krb5_ctx, NULL, + &krb5_child_extra_args); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_child_extra_args[0], uid_opt); + assert_string_equal(krb5_child_extra_args[1], gid_opt); + assert_string_equal(krb5_child_extra_args[2], "--chain-id=0"); + assert_null(krb5_child_extra_args[3]); + talloc_free(krb5_child_extra_args); + + krb5_ctx->canonicalize = true; + ret = set_extra_args(global_talloc_context, krb5_ctx, NULL, + &krb5_child_extra_args); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_child_extra_args[0], uid_opt); + assert_string_equal(krb5_child_extra_args[1], gid_opt); + assert_string_equal(krb5_child_extra_args[2], "--canonicalize"); + assert_string_equal(krb5_child_extra_args[3], "--chain-id=0"); + assert_null(krb5_child_extra_args[4]); + talloc_free(krb5_child_extra_args); + + krb5_ctx->realm = discard_const(TEST_REALM); + ret = set_extra_args(global_talloc_context, krb5_ctx, NULL, + &krb5_child_extra_args); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_child_extra_args[0], uid_opt); + assert_string_equal(krb5_child_extra_args[1], gid_opt); + assert_string_equal(krb5_child_extra_args[2], "--realm=" TEST_REALM); + assert_string_equal(krb5_child_extra_args[3], "--canonicalize"); + assert_string_equal(krb5_child_extra_args[4], "--chain-id=0"); + assert_null(krb5_child_extra_args[5]); + talloc_free(krb5_child_extra_args); + + /* --fast-principal will be only set if FAST is used */ + krb5_ctx->fast_principal = discard_const(TEST_FAST_PRINC); + ret = set_extra_args(global_talloc_context, krb5_ctx, NULL, + &krb5_child_extra_args); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_child_extra_args[0], uid_opt); + assert_string_equal(krb5_child_extra_args[1], gid_opt); + assert_string_equal(krb5_child_extra_args[2], "--realm=" TEST_REALM); + assert_string_equal(krb5_child_extra_args[3], "--canonicalize"); + assert_string_equal(krb5_child_extra_args[4], "--chain-id=0"); + assert_null(krb5_child_extra_args[5]); + talloc_free(krb5_child_extra_args); + + krb5_ctx->use_fast_str = discard_const(TEST_FAST_STR); + ret = set_extra_args(global_talloc_context, krb5_ctx, NULL, + &krb5_child_extra_args); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_child_extra_args[0], uid_opt); + assert_string_equal(krb5_child_extra_args[1], gid_opt); + assert_string_equal(krb5_child_extra_args[2], "--realm=" TEST_REALM); + assert_string_equal(krb5_child_extra_args[3], "--use-fast=" TEST_FAST_STR); + assert_string_equal(krb5_child_extra_args[4], + "--fast-principal=" TEST_FAST_PRINC); + assert_string_equal(krb5_child_extra_args[5], "--canonicalize"); + assert_string_equal(krb5_child_extra_args[6], "--chain-id=0"); + assert_null(krb5_child_extra_args[7]); + talloc_free(krb5_child_extra_args); + + krb5_ctx->lifetime_str = discard_const(TEST_LIFE_STR); + krb5_ctx->rlife_str = discard_const(TEST_RLIFE_STR); + ret = set_extra_args(global_talloc_context, krb5_ctx, NULL, + &krb5_child_extra_args); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_child_extra_args[0], uid_opt); + assert_string_equal(krb5_child_extra_args[1], gid_opt); + assert_string_equal(krb5_child_extra_args[2], "--realm=" TEST_REALM); + assert_string_equal(krb5_child_extra_args[3], "--lifetime=" TEST_LIFE_STR); + assert_string_equal(krb5_child_extra_args[4], + "--renewable-lifetime=" TEST_RLIFE_STR); + assert_string_equal(krb5_child_extra_args[5], "--use-fast=" TEST_FAST_STR); + assert_string_equal(krb5_child_extra_args[6], + "--fast-principal=" TEST_FAST_PRINC); + assert_string_equal(krb5_child_extra_args[7], "--canonicalize"); + assert_string_equal(krb5_child_extra_args[8], "--chain-id=0"); + assert_null(krb5_child_extra_args[9]); + talloc_free(krb5_child_extra_args); + + talloc_free(krb5_ctx); +} + +void test_sss_krb5_check_options(void **state) +{ + int ret; + struct dp_option *opts; + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + struct krb5_ctx *krb5_ctx; + + ret = sss_krb5_check_options(NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sss_krb5_get_options(test_ctx, test_ctx->tctx->confdb, + "[domain/" TEST_DOM_NAME "]", &opts); + assert_int_equal(ret, EOK); + assert_non_null(opts); + + krb5_ctx = talloc_zero(test_ctx, struct krb5_ctx); + assert_non_null(krb5_ctx); + + ret = sss_krb5_check_options(opts, test_ctx->tctx->dom, krb5_ctx); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_ctx->realm, TEST_DOM_NAME); + + /* check check_lifetime() indirectly */ + ret = dp_opt_set_string(opts, KRB5_LIFETIME, "123"); + assert_int_equal(ret, EOK); + ret = sss_krb5_check_options(opts, test_ctx->tctx->dom, krb5_ctx); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_ctx->lifetime_str, "123s"); + + ret = dp_opt_set_string(opts, KRB5_LIFETIME, "abc"); + assert_int_equal(ret, EOK); + ret = sss_krb5_check_options(opts, test_ctx->tctx->dom, krb5_ctx); + assert_int_equal(ret, EINVAL); + + ret = dp_opt_set_string(opts, KRB5_LIFETIME, "s"); + assert_int_equal(ret, EOK); + ret = sss_krb5_check_options(opts, test_ctx->tctx->dom, krb5_ctx); + assert_int_equal(ret, EINVAL); + + ret = dp_opt_set_string(opts, KRB5_LIFETIME, "1d"); + assert_int_equal(ret, EOK); + ret = sss_krb5_check_options(opts, test_ctx->tctx->dom, krb5_ctx); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_ctx->lifetime_str, "1d"); + + ret = dp_opt_set_string(opts, KRB5_LIFETIME, "7d 0h 0m 0s"); + assert_int_equal(ret, EOK); + ret = sss_krb5_check_options(opts, test_ctx->tctx->dom, krb5_ctx); + assert_int_equal(ret, EOK); + assert_string_equal(krb5_ctx->lifetime_str, "7d 0h 0m 0s"); + + /* check canonicalize */ + assert_false(krb5_ctx->canonicalize); + + ret = dp_opt_set_bool(opts, KRB5_USE_ENTERPRISE_PRINCIPAL, true); + assert_int_equal(ret, EOK); + ret = sss_krb5_check_options(opts, test_ctx->tctx->dom, krb5_ctx); + assert_int_equal(ret, EOK); + assert_true(krb5_ctx->canonicalize); + + ret = dp_opt_set_bool(opts, KRB5_USE_ENTERPRISE_PRINCIPAL, false); + assert_int_equal(ret, EOK); + ret = dp_opt_set_bool(opts, KRB5_CANONICALIZE, true); + assert_int_equal(ret, EOK); + ret = sss_krb5_check_options(opts, test_ctx->tctx->dom, krb5_ctx); + assert_int_equal(ret, EOK); + assert_true(krb5_ctx->canonicalize); + + ret = dp_opt_set_bool(opts, KRB5_USE_SUBDOMAIN_REALM, true); + assert_int_equal(ret, EOK); + + talloc_free(krb5_ctx); + talloc_free(opts); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_set_extra_args, + test_setup, test_teardown), + cmocka_unit_test_setup_teardown(test_sss_krb5_check_options, + test_setup, test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + + return rv; +} diff --git a/src/tests/cmocka/test_krb5_idp_plugin.c b/src/tests/cmocka/test_krb5_idp_plugin.c new file mode 100644 index 0000000..2d4f889 --- /dev/null +++ b/src/tests/cmocka/test_krb5_idp_plugin.c @@ -0,0 +1,230 @@ +/* + Copyright (C) 2020 Red Hat + + 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 "config.h" + +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "krb5_plugin/idp/idp.h" + +void test_sss_idp_oauth2_encode_challenge__null(void **state) +{ + struct sss_idp_oauth2 data = {0}; + char *str; + + str = sss_idp_oauth2_encode_challenge(NULL); + assert_null(str); + + str = sss_idp_oauth2_encode_challenge(&data); + assert_null(str); +} + +void test_sss_idp_oauth2_encode_challenge__ok(void **state) +{ + struct sss_idp_oauth2 data = {0}; + char *str; + + /* Empty required data. */ + data.verification_uri = NULL; + data.user_code = NULL; + str = sss_idp_oauth2_encode_challenge(&data); + assert_null(str); + + data.verification_uri = discard_const("https://visit.me"); + data.user_code = NULL; + str = sss_idp_oauth2_encode_challenge(&data); + assert_null(str); + + data.verification_uri = NULL; + data.user_code = discard_const("123456"); + str = sss_idp_oauth2_encode_challenge(&data); + assert_null(str); + + /* Empty optional data. */ + data.verification_uri = discard_const("https://visit.me"); + data.verification_uri_complete = NULL; + data.user_code = discard_const("123456"); + str = sss_idp_oauth2_encode_challenge(&data); + assert_non_null(str); + assert_string_equal(str, "oauth2 {\"verification_uri\":\"https://visit.me\",\"user_code\":\"123456\"}"); + free(str); + + /* Full format. */ + data.verification_uri = discard_const("https://visit.me"); + data.verification_uri_complete = discard_const("https://visit.me?code=123456"); + data.user_code = discard_const("123456"); + str = sss_idp_oauth2_encode_challenge(&data); + assert_non_null(str); + assert_string_equal(str, "oauth2 {\"verification_uri\":\"https://visit.me\",\"verification_uri_complete\":\"https://visit.me?code=123456\",\"user_code\":\"123456\"}"); + free(str); +} + +void test_sss_idp_oauth2_decode_challenge__invalid(void **state) +{ + struct sss_idp_oauth2 *data; + + data = sss_idp_oauth2_decode_challenge("not-json"); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge(""); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("{}"); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("[]"); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("[{}]"); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("{\"verification_uri\": \"test\", \"user_code\": \"test\"}"); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("oauth2 "); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("oauth2 {}"); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("oauth2 []"); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("oauth2 [{}]"); + assert_null(data); +} + +void test_sss_idp_oauth2_decode_challenge__ok(void **state) +{ + struct sss_idp_oauth2 *data; + + /* Empty required data. */ + data = sss_idp_oauth2_decode_challenge("oauth2 {\"verification_uri_complete\": \"https://visit.me?code=123456\",\"user_code\": \"123456\"}"); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("oauth2 {\"verification_uri\": \"https://visit.me\",\"verification_uri_complete\": \"https://visit.me?code=123456\"}"); + assert_null(data); + + data = sss_idp_oauth2_decode_challenge("oauth2 {\"verification_uri_complete\": \"https://visit.me?code=123456\"}"); + assert_null(data); + + /* Empty optional data. */ + data = sss_idp_oauth2_decode_challenge("oauth2 {\"verification_uri\": \"https://visit.me\",\"user_code\": \"123456\"}"); + assert_non_null(data); + assert_string_equal(data->verification_uri, "https://visit.me"); + assert_null(data->verification_uri_complete); + assert_string_equal(data->user_code, "123456"); + sss_idp_oauth2_free(data); + + /* Full format. */ + data = sss_idp_oauth2_decode_challenge("oauth2 {\"verification_uri\": \"https://visit.me\",\"verification_uri_complete\": \"https://visit.me?code=123456\",\"user_code\": \"123456\"}"); + assert_non_null(data); + assert_string_equal(data->verification_uri, "https://visit.me"); + assert_string_equal(data->verification_uri_complete, "https://visit.me?code=123456"); + assert_string_equal(data->user_code, "123456"); + sss_idp_oauth2_free(data); +} + +void test_sss_idp_config_init__invalid(void **state) +{ + struct sss_idp_config *idpcfg; + krb5_error_code ret; + + ret = sss_idp_config_init("not-json", &idpcfg); + assert_int_equal(ret, EINVAL); + + ret = sss_idp_config_init("", &idpcfg); + assert_int_equal(ret, EINVAL); + + ret = sss_idp_config_init("{}", &idpcfg); + assert_int_equal(ret, EINVAL); + + ret = sss_idp_config_init("[]", &idpcfg); + assert_int_equal(ret, EINVAL); + + ret = sss_idp_config_init("[{}]", &idpcfg); + assert_int_equal(ret, EINVAL); + + ret = sss_idp_config_init("[{\"indicators\": [\"test\"]}]", &idpcfg); + assert_int_equal(ret, EINVAL); + + ret = sss_idp_config_init("[{\"type\": \"oauth2\", \"indicators\": \"test\"}]", &idpcfg); + assert_int_equal(ret, EINVAL); +} + +void test_sss_idp_config_init__unsupported_type(void **state) +{ + struct sss_idp_config *idpcfg; + krb5_error_code ret; + + ret = sss_idp_config_init("{\"type\": null}", &idpcfg); + assert_int_equal(ret, EINVAL); + + ret = sss_idp_config_init("{\"type\": \"notsup\"}", &idpcfg); + assert_int_equal(ret, EINVAL); +} + +void test_sss_idp_config_init__oauth2(void **state) +{ + struct sss_idp_config *idpcfg; + krb5_error_code ret; + + ret = sss_idp_config_init("[{\"type\": \"oauth2\"}]", &idpcfg); + assert_int_equal(ret, 0); + assert_non_null(idpcfg); + assert_string_equal(idpcfg->type, "oauth2"); + assert_null(idpcfg->indicators); + sss_idp_config_free(idpcfg); + + ret = sss_idp_config_init("[{\"type\": \"oauth2\", \"indicators\": [\"i1\"]}]", &idpcfg); + assert_int_equal(ret, 0); + assert_non_null(idpcfg); + assert_string_equal(idpcfg->type, "oauth2"); + assert_non_null(idpcfg->indicators); + assert_non_null(idpcfg->indicators[0]); + assert_null(idpcfg->indicators[1]); + assert_string_equal(idpcfg->indicators[0], "i1"); + sss_idp_config_free(idpcfg); + + ret = sss_idp_config_init("[{\"type\": \"oauth2\", \"indicators\": [\"i1\", \"i2\"]}]", &idpcfg); + assert_int_equal(ret, 0); + assert_non_null(idpcfg); + assert_string_equal(idpcfg->type, "oauth2"); + assert_non_null(idpcfg->indicators); + assert_non_null(idpcfg->indicators[0]); + assert_non_null(idpcfg->indicators[1]); + assert_null(idpcfg->indicators[2]); + assert_string_equal(idpcfg->indicators[0], "i1"); + assert_string_equal(idpcfg->indicators[1], "i2"); + sss_idp_config_free(idpcfg); +} + +int main(int argc, const char *argv[]) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sss_idp_oauth2_encode_challenge__null), + cmocka_unit_test(test_sss_idp_oauth2_encode_challenge__ok), + cmocka_unit_test(test_sss_idp_oauth2_decode_challenge__invalid), + cmocka_unit_test(test_sss_idp_oauth2_decode_challenge__ok), + cmocka_unit_test(test_sss_idp_config_init__invalid), + cmocka_unit_test(test_sss_idp_config_init__unsupported_type), + cmocka_unit_test(test_sss_idp_config_init__oauth2), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_krb5_passkey_plugin.c b/src/tests/cmocka/test_krb5_passkey_plugin.c new file mode 100644 index 0000000..9593a65 --- /dev/null +++ b/src/tests/cmocka/test_krb5_passkey_plugin.c @@ -0,0 +1,470 @@ +/* + Copyright (C) 2023 Red Hat + + 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 "config.h" + +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "krb5_plugin/passkey/passkey.h" + +void test_sss_passkey_message_encode__null(void **state) +{ + char *str; + + str = sss_passkey_message_encode(NULL); + assert_null(str); +} + +void test_sss_passkey_message_encode__invalid(void **state) +{ + struct sss_passkey_message message = {0}; + char *str; + + message.phase = SSS_PASSKEY_PHASE_CHALLENGE; + str = sss_passkey_message_encode(&message); + assert_null(str); + + message.phase = SSS_PASSKEY_PHASE_REPLY; + str = sss_passkey_message_encode(&message); + assert_null(str); +} + +void test_sss_passkey_message_encode__init(void **state) +{ + struct sss_passkey_message message = {0}; + char *str; + + message.phase = SSS_PASSKEY_PHASE_INIT; + str = sss_passkey_message_encode(&message); + assert_non_null(str); + assert_string_equal(str, "passkey {\"phase\":0}"); + free(str); + + message.phase = SSS_PASSKEY_PHASE_INIT; + message.state = discard_const("abcd"); + str = sss_passkey_message_encode(&message); + assert_null(str); +} + +void test_sss_passkey_message_encode__challenge(void **state) +{ + struct sss_passkey_message message = {0}; + struct sss_passkey_challenge challenge = {0}; + const char *id_list[] = {"a", "b", NULL}; + char *str; + + message.phase = SSS_PASSKEY_PHASE_CHALLENGE; + message.state = NULL; + message.data.challenge = NULL; + str = sss_passkey_message_encode(&message); + assert_null(str); + + message.phase = SSS_PASSKEY_PHASE_CHALLENGE; + message.state = discard_const("abcd"); + message.data.challenge = NULL; + str = sss_passkey_message_encode(&message); + assert_null(str); + + challenge.domain = discard_const("domain"); + challenge.credential_id_list = discard_const(id_list); + challenge.user_verification = 1; + challenge.cryptographic_challenge = discard_const("crypto-challenge"); + message.phase = SSS_PASSKEY_PHASE_CHALLENGE; + message.state = NULL; + message.data.challenge = &challenge; + str = sss_passkey_message_encode(&message); + assert_null(str); + + challenge.domain = NULL; + challenge.credential_id_list = NULL; + challenge.user_verification = 0; + challenge.cryptographic_challenge = NULL; + message.phase = SSS_PASSKEY_PHASE_CHALLENGE; + message.state = NULL; + message.data.challenge = &challenge; + str = sss_passkey_message_encode(&message); + assert_null(str); + + challenge.domain = NULL; + challenge.credential_id_list = discard_const(id_list); + challenge.user_verification = 1; + challenge.cryptographic_challenge = discard_const("crypto-challenge"); + message.phase = SSS_PASSKEY_PHASE_CHALLENGE; + message.state = discard_const("abcd"); + message.data.challenge = &challenge; + str = sss_passkey_message_encode(&message); + assert_null(str); + + challenge.domain = discard_const("domain"); + challenge.credential_id_list = NULL; + challenge.user_verification = 1; + challenge.cryptographic_challenge = discard_const("crypto-challenge"); + message.phase = SSS_PASSKEY_PHASE_CHALLENGE; + message.state = discard_const("abcd"); + message.data.challenge = &challenge; + str = sss_passkey_message_encode(&message); + assert_null(str); + + challenge.domain = discard_const("domain"); + challenge.credential_id_list = discard_const(id_list); + challenge.user_verification = 1; + challenge.cryptographic_challenge = NULL; + message.phase = SSS_PASSKEY_PHASE_CHALLENGE; + message.state = discard_const("abcd"); + message.data.challenge = &challenge; + str = sss_passkey_message_encode(&message); + assert_null(str); + + challenge.domain = discard_const("domain"); + challenge.credential_id_list = discard_const(id_list); + challenge.user_verification = 1; + challenge.cryptographic_challenge = discard_const("crypto-challenge"); + message.phase = SSS_PASSKEY_PHASE_CHALLENGE; + message.state = discard_const("abcd"); + message.data.challenge = &challenge; + str = sss_passkey_message_encode(&message); + assert_non_null(str); + assert_string_equal(str, "passkey {\"phase\":1,\"state\":\"abcd\",\"data\":{\"domain\":\"domain\",\"credential_id_list\":[\"a\",\"b\"],\"user_verification\":1,\"cryptographic_challenge\":\"crypto-challenge\"}}"); + free(str); +} + +void test_sss_passkey_message_encode__reply(void **state) +{ + struct sss_passkey_message message = {0}; + struct sss_passkey_reply reply = {0}; + char *str; + + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = NULL; + message.data.reply = NULL; + str = sss_passkey_message_encode(&message); + assert_null(str); + + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = discard_const("abcd"); + message.data.reply = NULL; + str = sss_passkey_message_encode(&message); + assert_null(str); + + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = NULL; + message.data.reply = &reply; + str = sss_passkey_message_encode(&message); + assert_null(str); + + reply.credential_id = discard_const("id"); + reply.cryptographic_challenge = discard_const("crypto-challenge"); + reply.authenticator_data = discard_const("auth-data"); + reply.assertion_signature = discard_const("assertion-sig"); + reply.user_id = NULL; + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = NULL; + message.data.reply = &reply; + str = sss_passkey_message_encode(&message); + assert_null(str); + + reply.credential_id = NULL; + reply.cryptographic_challenge = discard_const("crypto-challenge"); + reply.authenticator_data = discard_const("auth-data"); + reply.assertion_signature = discard_const("assertion-sig"); + reply.user_id = NULL; + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = discard_const("abcd"); + message.data.reply = &reply; + str = sss_passkey_message_encode(&message); + assert_null(str); + + reply.credential_id = discard_const("id"); + reply.cryptographic_challenge = NULL; + reply.authenticator_data = discard_const("auth-data"); + reply.assertion_signature = discard_const("assertion-sig"); + reply.user_id = NULL; + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = discard_const("abcd"); + message.data.reply = &reply; + str = sss_passkey_message_encode(&message); + assert_null(str); + + reply.credential_id = discard_const("id"); + reply.cryptographic_challenge = discard_const("crypto-challenge"); + reply.authenticator_data = NULL; + reply.assertion_signature = discard_const("assertion-sig"); + reply.user_id = NULL; + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = discard_const("abcd"); + message.data.reply = &reply; + str = sss_passkey_message_encode(&message); + assert_null(str); + + reply.credential_id = discard_const("id"); + reply.cryptographic_challenge = discard_const("crypto-challenge"); + reply.authenticator_data = discard_const("auth-data"); + reply.assertion_signature = NULL; + reply.user_id = NULL; + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = discard_const("abcd"); + message.data.reply = &reply; + str = sss_passkey_message_encode(&message); + assert_null(str); + + reply.credential_id = discard_const("id"); + reply.cryptographic_challenge = discard_const("crypto-challenge"); + reply.authenticator_data = discard_const("auth-data"); + reply.assertion_signature = discard_const("assertion-sig"); + reply.user_id = NULL; + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = discard_const("abcd"); + message.data.reply = &reply; + str = sss_passkey_message_encode(&message); + assert_non_null(str); + assert_string_equal(str, "passkey {\"phase\":2,\"state\":\"abcd\",\"data\":{\"credential_id\":\"id\",\"cryptographic_challenge\":\"crypto-challenge\",\"authenticator_data\":\"auth-data\",\"assertion_signature\":\"assertion-sig\"}}"); + free(str); + + reply.credential_id = discard_const("id"); + reply.cryptographic_challenge = discard_const("crypto-challenge"); + reply.authenticator_data = discard_const("auth-data"); + reply.assertion_signature = discard_const("assertion-sig"); + reply.user_id = discard_const("user-id"); + message.phase = SSS_PASSKEY_PHASE_REPLY; + message.state = discard_const("abcd"); + message.data.reply = &reply; + str = sss_passkey_message_encode(&message); + assert_non_null(str); + assert_string_equal(str, "passkey {\"phase\":2,\"state\":\"abcd\",\"data\":{\"credential_id\":\"id\",\"cryptographic_challenge\":\"crypto-challenge\",\"authenticator_data\":\"auth-data\",\"assertion_signature\":\"assertion-sig\",\"user_id\":\"user-id\"}}"); + free(str); +} + +void test_sss_passkey_message_decode__null(void **state) +{ + struct sss_passkey_message *message; + + message = sss_passkey_message_decode(NULL); + assert_null(message); +} + +void test_sss_passkey_message_decode__invalid(void **state) +{ + struct sss_passkey_message *message; + const char *str; + + str = ""; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "oauth2"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":10}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":1, \"state\":\"abcd\"}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":1, \"state\":\"abcd\", \"data\":{\"test\":\"test\"}}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":2, \"state\":\"abcd\"}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":2, \"state\":\"abcd\", \"data\":{\"test\":\"test\"}}"; + message = sss_passkey_message_decode(str); + assert_null(message); +} + +void test_sss_passkey_message_decode__init(void **state) +{ + struct sss_passkey_message *message; + const char *str; + + str = "passkey {\"phase\":0,\"state\":\"abcd\"}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":0,\"data\":{\"test\":\"abcd\"}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":0,\"state\":\"abcd\",\"data\":{\"test\":\"abcd\"}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":0}"; + message = sss_passkey_message_decode(str); + assert_non_null(message); + assert_int_equal(message->phase, SSS_PASSKEY_PHASE_INIT); + assert_null(message->state); + assert_null(message->data.ptr); + sss_passkey_message_free(message); +} + +void test_sss_passkey_message_decode__challenge(void **state) +{ + struct sss_passkey_message *message; + const char *str; + + str = "passkey {\"phase\":1}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":1,\"state\":\"abcd\"}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":1,\"data\":{\"test\":\"abcd\"}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":1,\"state\":\"abcd\",\"data\":{\"domain\":\"domain\",\"credential_id_list\":[\"a\",\"b\"],\"user_verification\":1,\"cryptographic_challenge\":\"crypto-challenge\"}}"; + message = sss_passkey_message_decode(str); + assert_non_null(message); + assert_int_equal(message->phase, SSS_PASSKEY_PHASE_CHALLENGE); + assert_non_null(message->state); + assert_string_equal(message->state, "abcd"); + assert_non_null(message->data.challenge); + assert_non_null(message->data.challenge->domain); + assert_string_equal(message->data.challenge->domain, "domain"); + assert_non_null(message->data.challenge->credential_id_list); + assert_string_equal(message->data.challenge->credential_id_list[0], "a"); + assert_string_equal(message->data.challenge->credential_id_list[1], "b"); + assert_null(message->data.challenge->credential_id_list[2]); + assert_int_equal(message->data.challenge->user_verification, 1); + assert_non_null(message->data.challenge->cryptographic_challenge); + assert_string_equal(message->data.challenge->cryptographic_challenge, "crypto-challenge"); + sss_passkey_message_free(message); +} + +void test_sss_passkey_message_decode__reply(void **state) +{ + struct sss_passkey_message *message; + const char *str; + + str = "passkey {\"phase\":2}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":2,\"state\":\"abcd\"}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":2,\"data\":{\"test\":\"abcd\"}"; + message = sss_passkey_message_decode(str); + assert_null(message); + + str = "passkey {\"phase\":2,\"state\":\"abcd\",\"data\":{\"credential_id\":\"id\",\"cryptographic_challenge\":\"crypto-challenge\",\"authenticator_data\":\"auth-data\",\"assertion_signature\":\"assertion-sig\"}}"; + message = sss_passkey_message_decode(str); + assert_non_null(message); + assert_int_equal(message->phase, SSS_PASSKEY_PHASE_REPLY); + assert_non_null(message->state); + assert_string_equal(message->state, "abcd"); + assert_non_null(message->data.reply); + assert_non_null(message->data.reply->credential_id); + assert_string_equal(message->data.reply->credential_id, "id"); + assert_non_null(message->data.reply->cryptographic_challenge); + assert_string_equal(message->data.reply->cryptographic_challenge, "crypto-challenge"); + assert_non_null(message->data.reply->authenticator_data); + assert_string_equal(message->data.reply->authenticator_data, "auth-data"); + assert_non_null(message->data.reply->assertion_signature); + assert_string_equal(message->data.reply->assertion_signature, "assertion-sig"); + sss_passkey_message_free(message); +} + +void test_sss_passkey_config_init__invalid(void **state) +{ + struct sss_passkey_config *passkeycfg; + krb5_error_code ret; + + ret = sss_passkey_config_init("not-json", &passkeycfg); + assert_int_equal(ret, EINVAL); + + ret = sss_passkey_config_init("", &passkeycfg); + assert_int_equal(ret, EINVAL); + + ret = sss_passkey_config_init("[]", &passkeycfg); + assert_int_equal(ret, EINVAL); + + ret = sss_passkey_config_init("[{\"indicators\": \"test\"}]", &passkeycfg); + assert_int_equal(ret, EINVAL); + + ret = sss_passkey_config_init("{\"indicators\": \"test\"}", &passkeycfg); + assert_int_equal(ret, EINVAL); +} + +void test_sss_passkey_config_init__ok(void **state) +{ + struct sss_passkey_config *passkeycfg; + krb5_error_code ret; + + ret = sss_passkey_config_init("[{}]", &passkeycfg); + assert_int_equal(ret, 0); + assert_non_null(passkeycfg); + assert_null(passkeycfg->indicators); + sss_passkey_config_free(passkeycfg); + + ret = sss_passkey_config_init("[{\"indicators\": [\"i1\"]}]", &passkeycfg); + assert_int_equal(ret, 0); + assert_non_null(passkeycfg); + assert_non_null(passkeycfg->indicators); + assert_non_null(passkeycfg->indicators[0]); + assert_null(passkeycfg->indicators[1]); + assert_string_equal(passkeycfg->indicators[0], "i1"); + sss_passkey_config_free(passkeycfg); + + ret = sss_passkey_config_init("[{\"indicators\": [\"i1\", \"i2\"]}]", &passkeycfg); + assert_int_equal(ret, 0); + assert_non_null(passkeycfg); + assert_non_null(passkeycfg->indicators); + assert_non_null(passkeycfg->indicators[0]); + assert_non_null(passkeycfg->indicators[1]); + assert_null(passkeycfg->indicators[2]); + assert_string_equal(passkeycfg->indicators[0], "i1"); + assert_string_equal(passkeycfg->indicators[1], "i2"); + sss_passkey_config_free(passkeycfg); +} + +int main(int argc, const char *argv[]) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sss_passkey_message_encode__null), + cmocka_unit_test(test_sss_passkey_message_encode__invalid), + cmocka_unit_test(test_sss_passkey_message_encode__init), + cmocka_unit_test(test_sss_passkey_message_encode__challenge), + cmocka_unit_test(test_sss_passkey_message_encode__reply), + cmocka_unit_test(test_sss_passkey_message_decode__null), + cmocka_unit_test(test_sss_passkey_message_decode__invalid), + cmocka_unit_test(test_sss_passkey_message_decode__init), + cmocka_unit_test(test_sss_passkey_message_decode__challenge), + cmocka_unit_test(test_sss_passkey_message_decode__reply), + cmocka_unit_test(test_sss_passkey_config_init__invalid), + cmocka_unit_test(test_sss_passkey_config_init__ok), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_krb5_wait_queue.c b/src/tests/cmocka/test_krb5_wait_queue.c new file mode 100644 index 0000000..9f8473b --- /dev/null +++ b/src/tests/cmocka/test_krb5_wait_queue.c @@ -0,0 +1,365 @@ +/* + Copyright (C) 2015 Red Hat + + SSSD tests: Kerberos wait queue tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <stdlib.h> +#include <security/pam_modules.h> + +#include "util/util.h" +#include "providers/krb5/krb5_common.h" +#include "providers/krb5/krb5_auth.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_be.h" + +struct krb5_mocked_auth_state { + const char *user; + time_t us_delay; + int ret; + int pam_status; + int dp_err; +}; + +static void krb5_mocked_auth_done(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, + void *pvt); + +struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct pam_data *pd, + struct krb5_ctx *krb5_ctx) +{ + struct tevent_req *req; + struct krb5_mocked_auth_state *state; + struct tevent_timer *tt; + struct timeval tv; + + req = tevent_req_create(mem_ctx, &state, struct krb5_mocked_auth_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n"); + return NULL; + } + + state->user = sss_mock_ptr_type(const char *); + state->us_delay = sss_mock_type(time_t); + state->ret = sss_mock_type(int); + state->pam_status = sss_mock_type(int); + state->dp_err = sss_mock_type(int); + + tv = tevent_timeval_current_ofs(0, state->us_delay); + + tt = tevent_add_timer(ev, req, tv, krb5_mocked_auth_done, req); + if (tt == NULL) { + return NULL; + } + + return req; +} + +static void krb5_mocked_auth_done(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, + void *pvt) +{ + struct tevent_req *req; + struct krb5_mocked_auth_state *state; + + req = talloc_get_type(pvt, struct tevent_req); + state = tevent_req_data(req, struct krb5_mocked_auth_state); + + DEBUG(SSSDBG_TRACE_LIBS, "Finished auth request of %s\n", state->user); + + if (state->ret == 0) { + tevent_req_done(req); + } else { + tevent_req_error(req, state->ret); + } +} + +int krb5_auth_recv(struct tevent_req *req, + int *_pam_status, + int *_dp_err) +{ + struct krb5_mocked_auth_state *state; + + state = tevent_req_data(req, struct krb5_mocked_auth_state); + + if (_pam_status != NULL) { + *_pam_status = state->pam_status; + } + + if (_dp_err != NULL) { + *_dp_err = state->dp_err; + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +struct test_krb5_wait_queue { + struct sss_test_ctx *tctx; + int num_auths; + int num_finished_auths; + + struct be_ctx *be_ctx; + struct pam_data *pd; + struct krb5_ctx *krb5_ctx; +}; + +static int test_krb5_wait_queue_setup(void **state) +{ + struct test_krb5_wait_queue *test_ctx; + + test_ctx = talloc_zero(global_talloc_context, + struct test_krb5_wait_queue); + assert_non_null(test_ctx); + + test_ctx->tctx = create_ev_test_ctx(test_ctx); + assert_non_null(test_ctx); + + test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx); + assert_non_null(test_ctx->be_ctx); + + test_ctx->pd = talloc_zero(test_ctx, struct pam_data); + assert_non_null(test_ctx->pd); + + test_ctx->krb5_ctx = talloc_zero(test_ctx, struct krb5_ctx); + assert_non_null(test_ctx->krb5_ctx); + + *state = test_ctx; + return 0; +} + +static int test_krb5_wait_queue_teardown(void **state) +{ + struct test_krb5_wait_queue *test_ctx = + talloc_get_type(*state, struct test_krb5_wait_queue); + + talloc_free(test_ctx); + return 0; +} + +static void test_krb5_wait_mock(struct test_krb5_wait_queue *test_ctx, + const char *username, + time_t us_delay, + int ret, + int pam_status, + int dp_err) +{ + test_ctx->pd->user = discard_const(username); + + will_return(krb5_auth_send, username); + will_return(krb5_auth_send, us_delay); + will_return(krb5_auth_send, ret); + will_return(krb5_auth_send, pam_status); + will_return(krb5_auth_send, dp_err); +} + +static void test_krb5_wait_mock_success(struct test_krb5_wait_queue *test_ctx, + const char *username) +{ + return test_krb5_wait_mock(test_ctx, username, 200, 0, 0, 0); +} + +static void test_krb5_wait_queue_single_done(struct tevent_req *req); + +static void test_krb5_wait_queue_single(void **state) +{ + errno_t ret; + struct tevent_req *req; + struct test_krb5_wait_queue *test_ctx = + talloc_get_type(*state, struct test_krb5_wait_queue); + + test_krb5_wait_mock_success(test_ctx, "krb5_user"); + + req = krb5_auth_queue_send(test_ctx, + test_ctx->tctx->ev, + test_ctx->be_ctx, + test_ctx->pd, + test_ctx->krb5_ctx); + assert_non_null(req); + tevent_req_set_callback(req, test_krb5_wait_queue_single_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static void test_krb5_wait_queue_single_done(struct tevent_req *req) +{ + struct test_krb5_wait_queue *test_ctx = \ + tevent_req_callback_data(req, struct test_krb5_wait_queue); + errno_t ret; + int pam_status; + int dp_err; + + ret = krb5_auth_queue_recv(req, &pam_status, &dp_err); + talloc_free(req); + assert_int_equal(ret, EOK); + + test_ev_done(test_ctx->tctx, EOK); +} + +static void test_krb5_wait_queue_multi_done(struct tevent_req *req); + +static void test_krb5_wait_queue_multi(void **state) +{ + int i; + errno_t ret; + struct tevent_req *req; + struct test_krb5_wait_queue *test_ctx = + talloc_get_type(*state, struct test_krb5_wait_queue); + + test_ctx->num_auths = 1000; + + for (i=0; i < test_ctx->num_auths; i++) { + test_krb5_wait_mock_success(test_ctx, "krb5_user"); + + req = krb5_auth_queue_send(test_ctx, + test_ctx->tctx->ev, + test_ctx->be_ctx, + test_ctx->pd, + test_ctx->krb5_ctx); + assert_non_null(req); + tevent_req_set_callback(req, test_krb5_wait_queue_multi_done, test_ctx); + } + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static void test_krb5_wait_queue_multi_done(struct tevent_req *req) +{ + struct test_krb5_wait_queue *test_ctx = \ + tevent_req_callback_data(req, struct test_krb5_wait_queue); + errno_t ret; + int pam_status; + int dp_err; + + ret = krb5_auth_queue_recv(req, &pam_status, &dp_err); + talloc_free(req); + assert_int_equal(ret, EOK); + + test_ctx->num_finished_auths++; + + if (test_ctx->num_finished_auths == test_ctx->num_auths) { + test_ev_done(test_ctx->tctx, EOK); + } +} + +static void test_krb5_wait_queue_fail_odd_done(struct tevent_req *req); + +static void test_krb5_wait_queue_fail_odd(void **state) +{ + int i; + errno_t ret; + struct tevent_req *req; + struct test_krb5_wait_queue *test_ctx = + talloc_get_type(*state, struct test_krb5_wait_queue); + + test_ctx->num_auths = 10; + + for (i=0; i < test_ctx->num_auths; i++) { + test_krb5_wait_mock(test_ctx, "krb5_user", 0, i+1 % 2, PAM_SUCCESS, 0); + + req = krb5_auth_queue_send(test_ctx, + test_ctx->tctx->ev, + test_ctx->be_ctx, + test_ctx->pd, + test_ctx->krb5_ctx); + assert_non_null(req); + tevent_req_set_callback(req, test_krb5_wait_queue_fail_odd_done, test_ctx); + } + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static void test_krb5_wait_queue_fail_odd_done(struct tevent_req *req) +{ + struct test_krb5_wait_queue *test_ctx = \ + tevent_req_callback_data(req, struct test_krb5_wait_queue); + errno_t ret; + int pam_status; + int dp_err; + + ret = krb5_auth_queue_recv(req, &pam_status, &dp_err); + talloc_free(req); + assert_int_equal(ret, test_ctx->num_finished_auths+1 % 2); + + test_ctx->num_finished_auths++; + + if (test_ctx->num_finished_auths == test_ctx->num_auths) { + test_ev_done(test_ctx->tctx, EOK); + } +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + /* Run a single auth request */ + cmocka_unit_test_setup_teardown(test_krb5_wait_queue_single, + test_krb5_wait_queue_setup, + test_krb5_wait_queue_teardown), + + /* Run multiple auth requests */ + cmocka_unit_test_setup_teardown(test_krb5_wait_queue_multi, + test_krb5_wait_queue_setup, + test_krb5_wait_queue_teardown), + + /* Make sure that all requests in queue run even if some fail */ + cmocka_unit_test_setup_teardown(test_krb5_wait_queue_fail_odd, + test_krb5_wait_queue_setup, + test_krb5_wait_queue_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_ldap_auth.c b/src/tests/cmocka/test_ldap_auth.c new file mode 100644 index 0000000..470b35d --- /dev/null +++ b/src/tests/cmocka/test_ldap_auth.c @@ -0,0 +1,102 @@ +/* + Authors: + Pavel Reichl <preichl@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests - ldap auth + + 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 <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <setjmp.h> +#include <unistd.h> +#include <sys/types.h> +#include <cmocka.h> + +#include "tests/common.h" +#include "providers/ldap/ldap_auth.h" +#include "tests/cmocka/test_expire_common.h" + +struct check_pwexpire_policy_wrap_indata { + enum pwexpire type; + void *time_fmt; +}; + +static void check_pwexpire_policy_wrap(void *in, void *_out) +{ + errno_t ret; + struct check_pwexpire_policy_wrap_indata *data = + (struct check_pwexpire_policy_wrap_indata*) in; + + ret = check_pwexpire_policy(data->type, data->time_fmt, NULL, 0); + *(errno_t*)_out = ret; +} + +static void test_pwexpire_krb(void **state) +{ + struct expire_test_ctx *tc; + enum pwexpire type = PWEXPIRE_KERBEROS; + errno_t ret; + + tc = talloc_get_type(*state, struct expire_test_ctx); + assert_non_null(tc); + + ret = check_pwexpire_policy(type, + (void*) tc->invalid_longer_format, NULL, 0); + assert_int_equal(ret, ERR_TIMESPEC_NOT_SUPPORTED); + + ret = check_pwexpire_policy(type, (void*) tc->invalid_format, + NULL, 0); + assert_int_equal(ret, ERR_TIMESPEC_NOT_SUPPORTED); + + ret = check_pwexpire_policy(type, (void*) tc->past_time, + NULL, 0); + assert_int_equal(ret, ERR_PASSWORD_EXPIRED); + + ret = check_pwexpire_policy(type, (void*) tc->future_time, + NULL, 0); + assert_int_equal(ret, EOK); + + /* changing time zone has no effect as time of expiration is in UTC */ + struct check_pwexpire_policy_wrap_indata data; + data.type = type; + data.time_fmt = (void*)tc->future_time; + expire_test_tz("GST-2", + check_pwexpire_policy_wrap, + (void*)&data, + (void*)&ret); + assert_int_equal(ret, EOK); + + data.time_fmt = (void*)tc->past_time; + expire_test_tz("GST-2", + check_pwexpire_policy_wrap, + (void*)&data, + (void*)&ret); + assert_int_equal(ret, ERR_PASSWORD_EXPIRED); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_pwexpire_krb, + expire_test_setup, + expire_test_teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_ldap_id_cleanup.c b/src/tests/cmocka/test_ldap_id_cleanup.c new file mode 100644 index 0000000..ae5b0dd --- /dev/null +++ b/src/tests/cmocka/test_ldap_id_cleanup.c @@ -0,0 +1,348 @@ +/* + Authors: + Pavel Reichl <preichl@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests - id cleanup + + 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 <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <setjmp.h> +#include <unistd.h> +#include <sys/types.h> +#include <cmocka.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "providers/ldap/ldap_auth.h" +#include "tests/cmocka/test_expire_common.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/ldap_opts.h" +#include "providers/ipa/ipa_opts.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_FILE "tests_conf.ldb" + +struct sysdb_test_ctx { + struct sysdb_ctx *sysdb; + struct confdb_ctx *confdb; + struct tevent_context *ev; + struct sss_domain_info *domain; + struct sdap_options *opts; + struct sdap_id_ctx *id_ctx; +}; + +static int _setup_sysdb_tests(struct sysdb_test_ctx **ctx, bool enumerate) +{ + struct sysdb_test_ctx *test_ctx; + char *conf_db; + int ret; + + const char *val[2]; + val[1] = NULL; + + /* Create tests directory if it doesn't exist */ + /* (relative to current dir) */ + ret = mkdir(TESTS_PATH, 0775); + assert_true(ret == 0 || errno == EEXIST); + + test_ctx = talloc_zero(global_talloc_context, struct sysdb_test_ctx); + assert_non_null(test_ctx); + + /* Create an event context + * It will not be used except in confdb_init and sysdb_init + */ + test_ctx->ev = tevent_context_init(test_ctx); + assert_non_null(test_ctx->ev); + + conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_FILE); + assert_non_null(conf_db); + DEBUG(SSSDBG_MINOR_FAILURE, "CONFDB: %s\n", conf_db); + + /* Connect to the conf db */ + ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db); + assert_int_equal(ret, EOK); + + val[0] = "FILES"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/sssd", "domains", val); + assert_int_equal(ret, EOK); + + val[0] = "proxy"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/FILES", "id_provider", val); + assert_int_equal(ret, EOK); + + val[0] = enumerate ? "TRUE" : "FALSE"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/FILES", "enumerate", val); + assert_int_equal(ret, EOK); + + val[0] = "TRUE"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/FILES", "cache_credentials", val); + assert_int_equal(ret, EOK); + + ret = sssd_domain_init(test_ctx, test_ctx->confdb, "FILES", + TESTS_PATH, &test_ctx->domain); + assert_int_equal(ret, EOK); + + test_ctx->id_ctx = talloc_zero(test_ctx, struct sdap_id_ctx); + assert_non_null(test_ctx->id_ctx); + + test_ctx->domain->has_views = true; + test_ctx->sysdb = test_ctx->domain->sysdb; + + *ctx = test_ctx; + return EOK; +} + +#define setup_sysdb_tests(ctx) _setup_sysdb_tests((ctx), false) + +static int test_sysdb_setup(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + ret = setup_sysdb_tests(&test_ctx); + assert_int_equal(ret, EOK); + + test_ctx->domain->mpg_mode = MPG_DISABLED; + + /* set options */ + test_ctx->opts = talloc_zero(test_ctx, struct sdap_options); + assert_non_null(test_ctx->opts); + + ret = sdap_copy_map(test_ctx->opts, rfc2307_user_map, + SDAP_OPTS_USER, &test_ctx->opts->user_map); + assert_int_equal(ret, ERR_OK); + + ret = dp_copy_defaults(test_ctx->opts, default_basic_opts, + SDAP_OPTS_BASIC, &test_ctx->opts->basic); + assert_int_equal(ret, ERR_OK); + + dp_opt_set_int(test_ctx->opts->basic, SDAP_ACCOUNT_CACHE_EXPIRATION, 1); + + test_ctx->id_ctx->opts = test_ctx->opts; + + *state = (void *) test_ctx; + return 0; +} + +static int test_sysdb_teardown(void **state) +{ + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static errno_t invalidate_group(TALLOC_CTX *ctx, + struct sss_domain_info *domain, + const char *name) +{ + struct sysdb_attrs *sys_attrs = NULL; + errno_t ret; + + sys_attrs = sysdb_new_attrs(ctx); + if (sys_attrs) { + ret = sysdb_attrs_add_time_t(sys_attrs, + SYSDB_CACHE_EXPIRE, 1); + if (ret == EOK) { + ret = sysdb_set_group_attr(domain, name, sys_attrs, + SYSDB_MOD_REP); + } else { + DEBUG(SSSDBG_MINOR_FAILURE, + "Could not add expiration time to attributes\n"); + } + talloc_zfree(sys_attrs); + } else { + DEBUG(SSSDBG_MINOR_FAILURE, "Could not create sysdb attributes\n"); + ret = ENOMEM; + } + return ret; +} + +static void test_id_cleanup_exp_group(void **state) +{ + errno_t ret; + struct ldb_message *msg; + struct sdap_domain sdom; + char *special_grp; + char *empty_special_grp; + char *empty_grp; + char *grp; + char *test_user; + char *test_user2; + /* This timeout can be bigger because we will call invalidate_group + * to expire entries without waiting. */ + const uint64_t CACHE_TIMEOUT = 30; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + special_grp = sss_create_internal_fqname(test_ctx, + "special_gr*o/u\\p(2016)", + test_ctx->domain->name); + assert_non_null(special_grp); + + empty_special_grp = sss_create_internal_fqname(test_ctx, + "empty_gr*o/u\\p(2016)", + test_ctx->domain->name); + assert_non_null(empty_special_grp); + + empty_grp = sss_create_internal_fqname(test_ctx, "empty_grp", + test_ctx->domain->name); + assert_non_null(empty_grp); + + grp = sss_create_internal_fqname(test_ctx, "grp", test_ctx->domain->name); + assert_non_null(grp); + + test_user = sss_create_internal_fqname(test_ctx, "test_user", + test_ctx->domain->name); + assert_non_null(test_user); + test_user2 = sss_create_internal_fqname(test_ctx, "test_user2", + test_ctx->domain->name); + assert_non_null(test_user2); + + ret = sysdb_store_group(test_ctx->domain, special_grp, + 10002, NULL, CACHE_TIMEOUT, 0); + assert_int_equal(ret, EOK); + + ret = sysdb_store_group(test_ctx->domain, empty_special_grp, + 10003, NULL, CACHE_TIMEOUT, 0); + assert_int_equal(ret, EOK); + + ret = sysdb_store_group(test_ctx->domain, grp, + 10004, NULL, CACHE_TIMEOUT, 0); + assert_int_equal(ret, EOK); + + ret = sysdb_store_group(test_ctx->domain, empty_grp, + 10005, NULL, CACHE_TIMEOUT, 0); + assert_int_equal(ret, EOK); + + ret = sysdb_store_user(test_ctx->domain, test_user, NULL, + 10001, 10002, "Test user", + NULL, NULL, NULL, NULL, NULL, + 0, 0); + assert_int_equal(ret, EOK); + + ret = sysdb_store_user(test_ctx->domain, test_user2, NULL, + 10002, 10004, "Test user", + NULL, NULL, NULL, NULL, NULL, + 0, 0); + assert_int_equal(ret, EOK); + + sdom.dom = test_ctx->domain; + + /* not expired */ + ret = ldap_id_cleanup(test_ctx->id_ctx, &sdom); + assert_int_equal(ret, EOK); + + ret = sysdb_search_group_by_name(test_ctx, test_ctx->domain, + special_grp, NULL, &msg); + assert_int_equal(ret, EOK); + + ret = sysdb_search_group_by_name(test_ctx, test_ctx->domain, + empty_special_grp, NULL, &msg); + assert_int_equal(ret, EOK); + + ret = sysdb_search_group_by_name(test_ctx, test_ctx->domain, + grp, NULL, &msg); + assert_int_equal(ret, EOK); + + ret = sysdb_search_group_by_name(test_ctx, test_ctx->domain, + empty_grp, NULL, &msg); + assert_int_equal(ret, EOK); + + /* let records to expire */ + invalidate_group(test_ctx, test_ctx->domain, special_grp); + invalidate_group(test_ctx, test_ctx->domain, empty_special_grp); + invalidate_group(test_ctx, test_ctx->domain, grp); + invalidate_group(test_ctx, test_ctx->domain, empty_grp); + + ret = ldap_id_cleanup(test_ctx->id_ctx, &sdom); + assert_int_equal(ret, EOK); + + ret = sysdb_search_group_by_name(test_ctx, test_ctx->domain, + special_grp, NULL, &msg); + assert_int_equal(ret, EOK); + + ret = sysdb_search_group_by_name(test_ctx, test_ctx->domain, + empty_special_grp, NULL, &msg); + assert_int_equal(ret, ENOENT); + + ret = sysdb_search_group_by_name(test_ctx, test_ctx->domain, + grp, NULL, &msg); + assert_int_equal(ret, EOK); + + ret = sysdb_search_group_by_name(test_ctx, test_ctx->domain, + empty_grp, NULL, &msg); + assert_int_equal(ret, ENOENT); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + { "no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_id_cleanup_exp_group, + test_sysdb_setup, test_sysdb_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_FILE, "FILES"); + test_dom_suite_setup(TESTS_PATH); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + if (rv == 0 && no_cleanup == 0) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_FILE, "FILES"); + } + return rv; +} diff --git a/src/tests/cmocka/test_negcache.c b/src/tests/cmocka/test_negcache.c new file mode 100644 index 0000000..c80880c --- /dev/null +++ b/src/tests/cmocka/test_negcache.c @@ -0,0 +1,1437 @@ +/* + SSSD + + NSS Responder + + Authors: + Pallavi Jha <pallavikumarijha@gmail.com> + + Copyright (C) 2013 Red Hat + + 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 <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <setjmp.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <inttypes.h> +#include <cmocka.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" +#include "responder/nss/nss_private.h" +#include "sss_client/idmap/sss_nss_idmap.h" +#include "util/util_sss_idmap.h" +#include "lib/idmap/sss_idmap.h" +#include "util/util.h" +#include "responder/common/responder.h" +#include "responder/common/negcache.h" + +int test_ncache_setup(void **state); +int test_ncache_teardown(void **state); +void test_ncache_nocache_user(void **state); +void test_ncache_local_user(void **state); +void test_ncache_domain_user(void **state); +void test_ncache_both_user(void **state); +void test_ncache_nocache_uid(void **state); +void test_ncache_local_uid(void **state); +void test_ncache_domain_uid(void **state); +void test_ncache_both_uid(void **state); +void test_ncache_nocache_group(void **state); +void test_ncache_local_group(void **state); +void test_ncache_domain_group(void **state); +void test_ncache_both_group(void **state); +void test_ncache_nocache_gid(void **state); +void test_ncache_local_gid(void **state); +void test_ncache_domain_gid(void **state); +void test_ncache_both_gid(void **state); + +#define PORT 21 +#define SID "S-1-2-3-4-5" +#define CERT "MIIECTCCAvGgAwIBAgIBCTANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEuREVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA0MjgxMDIxMTFaFw0xNzA0MjgxMDIxMTFaMDIxEjAQBgNVBAoMCUlQQS5ERVZFTDEcMBoGA1UEAwwTaXBhLWRldmVsLmlwYS5kZXZlbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALIykqtHuAwTVEofHikG/9BQy/dfeZFlsTkBg2qtnnc78w3XufbcnkpJp9Bmcsy/d9beqf5nlsxJ8TcjLsRQ9Ou6YtQjTfM3OILuOz8s0ICbF6qb66bd9hX/BrLO/9+KnpWFSR+E/YEmzgYyDTbKfBWBaGuPPrOi/K6vwkRYFZVA/FYZkYDtQhFmBO884HYzS4P6frRH3PvtRqWNCmaHpe97dGKsvnM2ybT+IMSB8/54GajQr3+BciRh2XaT4wvSTxkXM1fUgrDxqAP2AZmpuIyDyboZh+rWOwbrTPfx5SipELZG3uHhP8HMcr4qQ8b20LWgxCRuT73sIooHET350xUCAwEAAaOCASYwggEiMB8GA1UdIwQYMBaAFPKdQk4PxEglWC8czg+hPyLIVciRMDsGCCsGAQUFBwEBBC8wLTArBggrBgEFBQcwAYYfaHR0cDovL2lwYS1jYS5pcGEuZGV2ZWwvY2Evb2NzcDAOBgNVHQ8BAf8EBAMCBPAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHQGA1UdHwRtMGswaaAxoC+GLWh0dHA6Ly9pcGEtY2EuaXBhLmRldmVsL2lwYS9jcmwvTWFzdGVyQ1JMLmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAdBgNVHQ4EFgQULSs/y/Wy/zIsqMIc3b2MgB7dMYIwDQYJKoZIhvcNAQELBQADggEBAJpHLlCnTR1TD8lxQgzl2n1JZOeryN/fAsGH0Vve2m8r5PC+ugnfAoULiuabBn1pOGxy/0x7Kg0/Iy8WRv8Fk7DqJCjXEqFXuFkZJfNDCtP9DzeNuMoV50iKoMfHS38BPFjXN+X/fSsBrA2fUWrlQCTmXlUN97gvQqxt5Slrxgukvxm9OSfu/sWz22LUvtJHupYwWv1iALgnXS86lAuVNYVALLxn34r58XsZlj5CSBMjBJWpaxEzgUdag3L2IPqOQXuPd0d8x11G9E/9gQquOSe2aiZjsdO/VYOCmzZsM2QPUMBVlBPDhfTVcWXQwN385uycW/ARtSzzSME2jKKWSIQ=" +#define PROTO "TCP" +#define SHORTSPAN 1 +#define NAME "foo_name" +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_nss_conf.ldb" +#define TEST_DOM_NAME "nss_test" +#define TEST_ID_PROVIDER "ldap" +#define TEST_SUBDOM_NAME "test.subdomain" + +/* register_cli_protocol_version is required in test since it links with + * responder_common.c module + */ +struct cli_protocol_version *register_cli_protocol_version(void) +{ + static struct cli_protocol_version responder_test_cli_protocol_version[] = { + {0, NULL, NULL} + }; + + return responder_test_cli_protocol_version; +} + +/* Mock NSS structure */ +static struct sss_nss_ctx * +mock_nctx(TALLOC_CTX *mem_ctx) +{ + struct sss_nss_ctx *nctx; + enum idmap_error_code err; + + nctx = talloc_zero(mem_ctx, struct sss_nss_ctx); + if (!nctx) { + return NULL; + } + + nctx->pwfield = discard_const("*"); + + err = sss_idmap_init(sss_idmap_talloc, nctx, sss_idmap_talloc_free, + &nctx->idmap_ctx); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, "sss_idmap_init failed.\n"); + talloc_free(nctx); + return NULL; + } + return nctx; +} + +struct test_state { + struct sss_nc_ctx *ctx; + struct sss_nss_ctx *nctx; + struct resp_ctx *rctx; +}; + +static int setup(void **state) +{ + int ret; + struct test_state *ts; + + test_dom_suite_setup(TESTS_PATH); + + ts = talloc(NULL, struct test_state); + assert_non_null(ts); + + ret = sss_ncache_init(ts, SHORTSPAN, 0, &ts->ctx); + assert_int_equal(ret, EOK); + assert_non_null(ts->ctx); + + *state = (void *)ts; + return 0; +} + +static int teardown(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + talloc_free(ts); + return 0; +} + +static void test_sss_ncache_init(void **state) +{ + int ret; + TALLOC_CTX *memctx; + struct sss_nc_ctx *ctx; + + memctx = talloc_new(NULL); + assert_non_null(memctx); + + ret = sss_ncache_init(memctx, SHORTSPAN, 0, &ctx); + assert_int_equal(ret, EOK); + assert_non_null(ctx); + + talloc_free(memctx); +} + +/* @test_sss_ncache_uid : test following functions + * sss_ncache_set_uid + * sss_ncache_check_uid + */ +static void test_sss_ncache_uid(void **state) +{ + uid_t uid; + int ret; + bool permanent; + struct test_state *ts; + + uid = getuid(); + + ts = talloc_get_type_abort(*state, struct test_state); + + /* test when uid not present in database */ + ret = sss_ncache_check_uid(ts->ctx, NULL, uid); + assert_int_equal(ret, ENOENT); + + /* test when uid is present in database */ + permanent = true; + + ret = sss_ncache_reset_permanent(ts->ctx); + assert_int_equal(ret, EOK); + + ret = sss_ncache_set_uid(ts->ctx, permanent, NULL, uid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_uid(ts->ctx, NULL, uid); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_set_uid(ts->ctx, permanent, NULL, uid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_uid(ts->ctx, NULL, uid); + assert_int_equal(ret, EEXIST); + + sleep(SHORTSPAN + 1); + + ret = sss_ncache_check_uid(ts->ctx, NULL, uid); + assert_int_equal(ret, EEXIST); + + permanent = false; + + ret = sss_ncache_set_uid(ts->ctx, permanent, NULL, uid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_uid(ts->ctx, NULL, uid); + assert_int_equal(ret, EEXIST); + + sleep(SHORTSPAN + 1); + + ret = sss_ncache_check_uid(ts->ctx, NULL, uid); + assert_int_equal(ret, ENOENT); + + ret = sss_ncache_set_uid(ts->ctx, permanent, NULL, uid); + assert_int_equal(ret, EOK); +} + +/* @test_sss_ncache_gid : test following functions + * sss_ncache_set_gid + * sss_ncache_check_gid + */ +static void test_sss_ncache_gid(void **state) +{ + gid_t gid; + int ret; + bool permanent; + struct test_state *ts; + + gid = getgid(); + ts = talloc_get_type_abort(*state, struct test_state); + + /* test when gid is not present in database */ + ret = sss_ncache_check_gid(ts->ctx, NULL, gid); + assert_int_equal(ret, ENOENT); + + /* test when gid is present in database */ + permanent = true; + ret = sss_ncache_set_gid(ts->ctx, permanent, NULL, gid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_gid(ts->ctx, NULL, gid); + assert_int_equal(ret, EEXIST); + + permanent = false; + ret = sss_ncache_set_gid(ts->ctx, permanent, NULL, gid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_gid(ts->ctx, NULL, gid); + assert_int_equal(ret, EEXIST); +} + + +/* @test_sss_ncache_sid : test following functions + * sss_ncache_set_sid + * sss_ncache_check_sid + */ +static void test_sss_ncache_sid(void **state) +{ + int ret; + bool permanent; + const char *sid = NULL; + struct test_state *ts; + + sid = SID; + ts = talloc_get_type_abort(*state, struct test_state); + + /*test when sid in not present in database */ + ret = sss_ncache_check_sid(ts->ctx, NULL, sid); + assert_int_equal(ret, ENOENT); + + /* test when sid is present in database */ + permanent = true; + ret = sss_ncache_set_sid(ts->ctx, permanent, NULL, sid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_sid(ts->ctx, NULL, sid); + assert_int_equal(ret, EEXIST); + + permanent = false; + ret = sss_ncache_set_sid(ts->ctx, permanent, NULL, sid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_sid(ts->ctx, NULL, sid); + assert_int_equal(ret, EEXIST); +} + +/* @test_sss_ncache_cert : test following functions + * sss_ncache_set_cert + * sss_ncache_check_cert_ + */ +static void test_sss_ncache_cert(void **state) +{ + int ret; + bool permanent; + const char *cert = NULL; + struct test_state *ts; + + cert = CERT; + ts = talloc_get_type_abort(*state, struct test_state); + + /*test when cert in not present in database */ + ret = sss_ncache_check_cert(ts->ctx, cert); + assert_int_equal(ret, ENOENT); + + /* test when cert is present in database */ + permanent = true; + ret = sss_ncache_set_cert(ts->ctx, permanent, cert); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_cert(ts->ctx, cert); + assert_int_equal(ret, EEXIST); + + permanent = false; + ret = sss_ncache_set_cert(ts->ctx, permanent, cert); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_cert(ts->ctx, cert); + assert_int_equal(ret, EEXIST); +} + +/* @test_sss_ncache_user : test following functions + * sss_ncache_check_user + * sss_ncache_set_user + */ +static void test_sss_ncache_user(void **state) +{ + int ret; + bool permanent; + char *name; + struct test_state *ts; + struct sss_domain_info *dom; + + ts = talloc_get_type_abort(*state, struct test_state); + dom = talloc(ts, struct sss_domain_info); + dom->name = discard_const_p(char, TEST_DOM_NAME); + + name = sss_create_internal_fqname(ts, NAME, dom->name); + assert_non_null(name); + + /* test when domain name is not present in database */ + dom->case_sensitive = false; + ret = sss_ncache_check_user(ts->ctx, dom, name); + assert_int_equal(ret, ENOENT); + + dom->case_sensitive = true; + ret = sss_ncache_check_user(ts->ctx, dom, name); + assert_int_equal(ret, ENOENT); + + /* test when domain name is present in database */ + permanent = true; + ret = sss_ncache_set_user(ts->ctx, permanent, dom, name); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_user(ts->ctx, dom, name); + assert_int_equal(ret, EEXIST); + + permanent = false; + ret = sss_ncache_set_user(ts->ctx, permanent, dom, name); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_user(ts->ctx, dom, name); + assert_int_equal(ret, EEXIST); + + talloc_free(name); +} + +/* @test_sss_ncache_group : test following functions + * sss_ncache_check_group + * sss_ncache_set_group + */ +static void test_sss_ncache_group(void **state) +{ + int ret; + bool permanent; + char *name; + struct test_state *ts; + struct sss_domain_info *dom; + + ts = talloc_get_type_abort(*state, struct test_state); + dom = talloc(ts, struct sss_domain_info); + dom->name = discard_const_p(char, TEST_DOM_NAME); + + name = sss_create_internal_fqname(ts, NAME, dom->name); + assert_non_null(name); + + /* test when domain name is not present in database */ + dom->case_sensitive = false; + ret = sss_ncache_check_group(ts->ctx, dom, name); + assert_int_equal(ret, ENOENT); + + dom->case_sensitive = true; + ret = sss_ncache_check_group(ts->ctx, dom, name); + assert_int_equal(ret, ENOENT); + + /* test when domain name is present in database */ + permanent = true; + ret = sss_ncache_set_group(ts->ctx, permanent, dom, name); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_group(ts->ctx, dom, name); + assert_int_equal(ret, EEXIST); + + permanent = false; + ret = sss_ncache_set_group(ts->ctx, permanent, dom, name); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_group(ts->ctx, dom, name); + assert_int_equal(ret, EEXIST); + + talloc_free(name); +} + +/* @test_sss_ncache_netgr : test following functions + * sss_ncache_check_netgr + * sss_ncache_set_netgr + */ +static void test_sss_ncache_netgr(void **state) +{ + int ret; + bool permanent; + const char *name = NAME; + struct test_state *ts; + struct sss_domain_info *dom; + + ts = talloc_get_type_abort(*state, struct test_state); + dom = talloc(ts, struct sss_domain_info); + dom->name = discard_const_p(char, TEST_DOM_NAME); + + /* test when domain name is not present in database */ + dom->case_sensitive = false; + ret = sss_ncache_check_netgr(ts->ctx, dom, name); + assert_int_equal(ret, ENOENT); + + dom->case_sensitive = true; + ret = sss_ncache_check_netgr(ts->ctx, dom, name); + assert_int_equal(ret, ENOENT); + + /* test when domain name is present in database */ + permanent = true; + ret = sss_ncache_set_netgr(ts->ctx, permanent, dom, name); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_netgr(ts->ctx, dom, name); + assert_int_equal(ret, EEXIST); + + permanent = false; + ret = sss_ncache_set_netgr(ts->ctx, permanent, dom, name); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_netgr(ts->ctx, dom, name); + assert_int_equal(ret, EEXIST); +} + +/* @test_sss_ncache_service_name : test following functions + * sss_ncache_check_service + * sss_ncache_set_service_name + */ +static void test_sss_ncache_service_name(void **state) +{ + int ret; + bool permanent; + const char *name = NAME; + struct test_state *ts; + struct sss_domain_info *dom; + + ts = talloc_get_type_abort(*state, struct test_state); + dom = talloc(ts, struct sss_domain_info); + dom->name = discard_const_p(char, TEST_DOM_NAME); + + /* test when domain name and protocol are not present in database */ + dom->case_sensitive = false; + ret = sss_ncache_check_service(ts->ctx, dom, name, PROTO); + assert_int_equal(ret, ENOENT); + + dom->case_sensitive = true; + ret = sss_ncache_check_service(ts->ctx, dom, name, PROTO); + assert_int_equal(ret, ENOENT); + + /* test when domain name and protocol are present in database */ + permanent = true; + ret = sss_ncache_set_service_name(ts->ctx, permanent, dom, name, PROTO); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_service(ts->ctx, dom, name, PROTO); + assert_int_equal(ret, EEXIST); + + permanent = false; + ret = sss_ncache_set_service_name(ts->ctx, permanent, dom, name, PROTO); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_service(ts->ctx, dom, name, PROTO); + assert_int_equal(ret, EEXIST); +} + +/* @test_sss_ncache_service_port : test following functions + * sss_ncache_check_service_port + * sss_ncache_set_service_port + */ +static void test_sss_ncache_service_port(void **state) +{ + int ret; + bool permanent; + struct test_state *ts; + struct sss_domain_info *dom; + + ts = talloc_get_type_abort(*state, struct test_state); + dom = talloc(ts, struct sss_domain_info); + dom->name = discard_const_p(char, TEST_DOM_NAME); + + /* test when domain name, port and protocol are not present in database */ + dom->case_sensitive = false; + ret = sss_ncache_check_service_port(ts->ctx, dom, (uint16_t)PORT, + PROTO); + assert_int_equal(ret, ENOENT); + + dom->case_sensitive = true; + ret = sss_ncache_check_service_port(ts->ctx, dom, (uint16_t)PORT, + PROTO); + assert_int_equal(ret, ENOENT); + + /* test when domain name, port and protocol are present in database */ + permanent = true; + ret = sss_ncache_set_service_port(ts->ctx, permanent, dom, (uint16_t)PORT, + PROTO); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_service_port(ts->ctx, dom, (uint16_t)PORT, + PROTO); + assert_int_equal(ret, EEXIST); + + permanent = false; + ret = sss_ncache_set_service_port(ts->ctx, permanent, dom, (uint16_t)PORT, + PROTO); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_service_port(ts->ctx, dom, (uint16_t)PORT, + PROTO); + assert_int_equal(ret, EEXIST); +} + + +static void test_sss_ncache_reset_permanent(void **state) +{ + int ret; + struct test_state *ts; + const bool permanent = true; + + ts = talloc_get_type_abort(*state, struct test_state); + + ret = sss_ncache_set_uid(ts->ctx, permanent, NULL, 0); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_uid(ts->ctx, NULL, 0); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_reset_permanent(ts->ctx); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_uid(ts->ctx, NULL, 0); + assert_int_equal(ret, ENOENT); +} + +static int check_user_in_ncache(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *name) +{ + char *fqdn; + int ret; + + fqdn = sss_create_internal_fqname(ctx, name, dom->name); + ret = sss_ncache_check_user(ctx, dom, fqdn); + talloc_free(fqdn); + return ret; +} + +static int check_group_in_ncache(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *name) +{ + char *fqdn; + int ret; + + fqdn = sss_create_internal_fqname(ctx, name, dom->name); + ret = sss_ncache_check_group(ctx, dom, fqdn); + talloc_free(fqdn); + return ret; +} + +static int check_uid_in_ncache(struct sss_nc_ctx *ctx, + uid_t uid) +{ + int ret; + + ret = sss_ncache_check_uid(ctx, NULL, uid); + return ret; +} + +static int check_gid_in_ncache(struct sss_nc_ctx *ctx, + gid_t gid) +{ + int ret; + + ret = sss_ncache_check_gid(ctx, NULL, gid); + return ret; +} + +static int add_confdb_params(struct sss_test_conf_param params[], + struct confdb_ctx *cdb, const char *section) +{ + const char *val[2]; + int ret; + + val[1] = NULL; + + for (int i = 0; params[i].key; i++) { + val[0] = params[i].value; + ret = confdb_add_param(cdb, true, section, params[i].key, val); + assert_int_equal(ret, EOK); + } + + return EOK; +} + +static int add_nss_params(struct sss_test_conf_param nss_params[], + struct confdb_ctx *cdb) +{ + return add_confdb_params(nss_params, cdb, CONFDB_NSS_CONF_ENTRY); +} + +static void test_sss_ncache_prepopulate(void **state) +{ + int ret; + struct test_state *ts; + struct tevent_context *ev; + struct sss_nc_ctx *ncache; + struct sss_test_ctx *tc; + const char *const testdom[4] = { TEST_SUBDOM_NAME, "TEST.SUB", "test", "S-3" }; + struct sss_domain_info *subdomain; + + struct sss_test_conf_param nss_params[] = { + { "filter_users", "testuser_nss@UPN.REALM, testuser_nss_short, all_dom_upn@"TEST_DOM_NAME }, + { NULL, NULL }, + }; + struct sss_test_conf_param dom_params[] = { + { "filter_users", "testuser1, testuser2@"TEST_DOM_NAME", testuser3@somedomain" }, + { "filter_groups", "testgroup1, testgroup2@"TEST_DOM_NAME", testgroup3@somedomain" }, + { NULL, NULL }, + }; + + ts = talloc_get_type_abort(*state, struct test_state); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + ts->nctx = mock_nctx(ts); + assert_non_null(ts->nctx); + + tc = create_dom_test_ctx(ts, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, TEST_ID_PROVIDER, dom_params); + assert_non_null(tc); + + ret = add_nss_params(nss_params, tc->confdb); + assert_int_equal(ret, EOK); + + subdomain = new_subdomain(tc, tc->dom, + testdom[0], testdom[1], testdom[2], testdom[0], + testdom[3], false, false, NULL, NULL, 0, + tc->confdb, true); + assert_non_null(subdomain); + + ret = sysdb_subdomain_store(tc->sysdb, + testdom[0], testdom[1], testdom[2], testdom[0], + testdom[3], false, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(tc->dom, tc->confdb); + assert_int_equal(ret, EOK); + + ncache = ts->ctx; + ts->rctx = mock_rctx(ts, ev, tc->dom, ts->nctx); + assert_non_null(ts->rctx); + + ret = sss_names_init(ts, tc->confdb, TEST_DOM_NAME, &tc->dom->names); + assert_int_equal(ret, EOK); + + ret = sss_ncache_prepopulate(ncache, tc->confdb, ts->rctx); + assert_int_equal(ret, EOK); + + sleep(SHORTSPAN); + + ret = check_user_in_ncache(ncache, tc->dom, "testuser1"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, tc->dom, "testgroup1"); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, tc->dom, "testuser2"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, tc->dom, "testgroup2"); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, tc->dom, "testuser3"); + assert_int_equal(ret, ENOENT); + + ret = check_group_in_ncache(ncache, tc->dom, "testgroup3"); + assert_int_equal(ret, ENOENT); + + ret = check_user_in_ncache(ncache, tc->dom, "testuser3@somedomain"); + assert_int_equal(ret, ENOENT); + + ret = sss_ncache_check_upn(ncache, tc->dom, "testuser3@somedomain"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, tc->dom, "testgroup3@somedomain"); + assert_int_equal(ret, ENOENT); + + ret = check_user_in_ncache(ncache, tc->dom, "root"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, tc->dom, "root"); + assert_int_equal(ret, EEXIST); + + ret = check_uid_in_ncache(ncache, 0); + assert_int_equal(ret, EEXIST); + + ret = check_gid_in_ncache(ncache, 0); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_upn(ncache, tc->dom, "testuser_nss@UPN.REALM"); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_upn(ncache, tc->dom->subdomains, "testuser_nss@UPN.REALM"); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_upn(ncache, tc->dom, "testuser_nss_short@" TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_upn(ncache, tc->dom->subdomains, "testuser_nss_short@" TEST_SUBDOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, tc->dom, "testuser_nss_short"); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, tc->dom->subdomains, "testuser_nss_short"); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_upn(ncache, tc->dom, "testuser1@" TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_upn(ncache, tc->dom, "testuser2@" TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_upn(ncache, tc->dom, "testuser3@somedomain"); + assert_int_equal(ret, EEXIST); + + /* Fully qualified names with a known domain part should be added to all + * negative UPN caches and to the negative cache of the know domain. */ + ret = sss_ncache_check_upn(ncache, tc->dom, "all_dom_upn@"TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_upn(ncache, tc->dom->subdomains, + "all_dom_upn@"TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, tc->dom, "all_dom_upn"); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, tc->dom->subdomains, "all_dom_upn"); + assert_int_equal(ret, ENOENT); +} + +static void test_sss_ncache_default_domain_suffix(void **state) +{ + int ret; + struct test_state *ts; + struct tevent_context *ev; + struct sss_nc_ctx *ncache; + struct sss_test_ctx *tc; + struct sss_domain_info *dom; + + struct sss_test_conf_param params[] = { + { "filter_users", "testuser1, testuser2@"TEST_DOM_NAME", testuser3@somedomain" }, + { "filter_groups", "testgroup1, testgroup2@"TEST_DOM_NAME", testgroup3@somedomain" }, + { NULL, NULL }, + }; + + ts = talloc_get_type_abort(*state, struct test_state); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + dom = talloc_zero(ts, struct sss_domain_info); + assert_non_null(dom); + dom->name = discard_const_p(char, TEST_DOM_NAME); + + ts->nctx = mock_nctx(ts); + assert_non_null(ts->nctx); + + tc = create_dom_test_ctx(ts, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, TEST_ID_PROVIDER, params); + assert_non_null(tc); + + ncache = ts->ctx; + ts->rctx = mock_rctx(ts, ev, dom, ts->nctx); + assert_non_null(ts->rctx); + ts->rctx->default_domain = discard_const(TEST_DOM_NAME); + + ret = sss_names_init(ts, tc->confdb, TEST_DOM_NAME, &dom->names); + assert_int_equal(ret, EOK); + + ret = sss_ncache_prepopulate(ncache, tc->confdb, ts->rctx); + assert_int_equal(ret, EOK); + + ret = check_user_in_ncache(ncache, dom, "testuser1"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, dom, "testgroup1"); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, dom, "testuser2"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, dom, "testgroup2"); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, dom, "testuser3"); + assert_int_equal(ret, ENOENT); + + ret = check_group_in_ncache(ncache, dom, "testgroup3"); + assert_int_equal(ret, ENOENT); + +} + +static void test_sss_ncache_reset_prepopulate(void **state) +{ + int ret; + struct test_state *ts; + struct tevent_context *ev; + struct sss_nc_ctx *ncache; + struct sss_test_ctx *tc; + struct sss_domain_info *dom; + struct sss_domain_info *dom2; + + struct sss_test_conf_param params[] = { + { "filter_users", "testuser1@"TEST_DOM_NAME", testuser2@"TEST_DOM_NAME"2" }, + { "filter_groups", "testgroup1@"TEST_DOM_NAME", testgroup2@"TEST_DOM_NAME"2" }, + { NULL, NULL }, + }; + + const char *sss_nss_filter_users[] = { params[0].value, NULL}; + const char *sss_nss_filter_groups[] = { params[1].value, NULL}; + + ts = talloc_get_type_abort(*state, struct test_state); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + dom = talloc_zero(ts, struct sss_domain_info); + assert_non_null(dom); + dom->name = discard_const_p(char, TEST_DOM_NAME); + + ts->nctx = mock_nctx(ts); + assert_non_null(ts->nctx); + + tc = create_dom_test_ctx(ts, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, TEST_ID_PROVIDER, params); + assert_non_null(tc); + + ret = confdb_add_param(tc->confdb, true, "config/nss", + "filter_users", sss_nss_filter_users); + assert_int_equal(ret, EOK); + + ret = confdb_add_param(tc->confdb, true, "config/nss", + "filter_groups", sss_nss_filter_groups); + assert_int_equal(ret, EOK); + + ncache = ts->ctx; + ts->rctx = mock_rctx(ts, ev, dom, ts->nctx); + assert_non_null(ts->rctx); + ts->rctx->default_domain = discard_const(TEST_DOM_NAME); + ts->rctx->cdb = tc->confdb; + + ret = sss_names_init(ts, tc->confdb, TEST_DOM_NAME, &dom->names); + assert_int_equal(ret, EOK); + + ret = sss_ncache_reset_repopulate_permanent(ts->rctx, ncache); + assert_int_equal(ret, EOK); + + /* Add another domain */ + dom2 = talloc_zero(ts, struct sss_domain_info); + assert_non_null(dom2); + dom2->name = discard_const_p(char, TEST_DOM_NAME"2"); + dom->next = dom2; + dom2->names = dom->names; + + /* First domain should not be known, the second not */ + ret = check_user_in_ncache(ncache, dom, "testuser1"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, dom, "testgroup1"); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, dom2, "testuser2"); + assert_int_equal(ret, ENOENT); + + ret = check_group_in_ncache(ncache, dom2, "testgroup2"); + assert_int_equal(ret, ENOENT); + + ret = sss_ncache_reset_repopulate_permanent(ts->rctx, ncache); + assert_int_equal(ret, EOK); + + /* First domain should not be known, the second not */ + ret = check_user_in_ncache(ncache, dom, "testuser1"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, dom, "testgroup1"); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, dom2, "testuser2"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, dom2, "testgroup2"); + assert_int_equal(ret, EEXIST); +} + +/* The main purpose of test_sss_ncache_short_name_in_domain is to test that + * short names in the filter_users or filter_groups options in a [domain/...] + * section are properly added to the related sub-domains as well (if there are + * any) and not added to domains from other [domain/...] sections. For + * completeness entries with fully-qualified names of the parent and the + * sub-domain and the generic UPN are added as well. + * + * The result should of course be independent of the present domains. To + * verify this the domains are added one after the other and the negative + * cache is repopulated each time. The result should be also independent of + * the setting of default_domain_suffix option which is tested by + * test_sss_ncache_short_name_in_domain_with_prefix. + * + * With the given domains, users and group we have to following expectations: + * - the short name entry will be added to the domain and all sub-domains as + * name and as upn by expanding it to a fully-qualified name with the + * domain name or sub-domain name respectively + * - the fully-qualified name from the parent domain is added as name and upn + * to the parent domain and as upn to all sub-domains + * - the fully-qualified name from the sub-domain is added as name to the + * sub-domain and as upn to the parent and all sub-domains + * - the generic upn is nowhere added as name and as upn to the parent and all + * sub-domains + * - none of the names is added to a different parent domain + * + * The following table should illustrated the expectations: + * + * user (name): + * | shortuser | parentu@TEST_DOM_NAME | subdomu@subTEST_DOM_NAME | upn@upn.dom + *-----------------+-----------+-----------------------+--------------------------+------------ + * TEST_DOM_NAME | PRESENT | PRESENT | MISSING | MISSING + * subTEST_DOM_NAME| PRESENT | MISSING | PRESENT | MISSING + * TEST_DOM_NAME2 | MISSING | MISSING | MISSING | MISSING + * + * user (upn): + * | shortuser | parentu@TEST_DOM_NAME | subdomu@subTEST_DOM_NAME | upn@upn.dom + *-----------------+-----------+-----------------------+--------------------------+------------ + * TEST_DOM_NAME | PRESENT | PRESENT | PRESENT | PRESENT + * subTEST_DOM_NAME| PRESENT | PRESENT | PRESENT | PRESENT + * TEST_DOM_NAME2 | MISSING | MISSING | MISSING | MISSING + * + * + * + * groups: + * | shortgroup | parentg@TEST_DOM_NAME | subdomg@subTEST_DOM_NAME + *-----------------+------------+-----------------------+------------------------- + * TEST_DOM_NAME | PRESENT | PRESENT | MISSING + * subTEST_DOM_NAME| PRESENT | MISSING | PRESENT + * TEST_DOM_NAME2 | MISSING | MISSING | MISSING + * + * + * The following expect_*() implement checks for the expextations: + */ + +static void expect_in_parent(struct sss_nc_ctx *ncache, + struct sss_domain_info *dom) +{ + int ret; + + ret = check_user_in_ncache(ncache, dom, "shortuser"); + assert_int_equal(ret, EEXIST); + ret = sss_ncache_check_upn(ncache, dom, "shortuser@"TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, dom, "parentu"); + assert_int_equal(ret, EEXIST); + ret = sss_ncache_check_upn(ncache, dom, "parentu@"TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, dom, "subdomu"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_upn(ncache, dom, "subdomu@sub"TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, dom, "upn"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_upn(ncache, dom, "upn@upn.dom"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, dom, "shortgroup"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, dom, "parentg"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, dom, "subdomg"); + assert_int_equal(ret, ENOENT); +} + +static void expect_in_subdomain(struct sss_nc_ctx *ncache, + struct sss_domain_info *sub_dom) +{ + int ret; + + ret = check_user_in_ncache(ncache, sub_dom, "shortuser"); + assert_int_equal(ret, EEXIST); + ret = sss_ncache_check_upn(ncache, sub_dom, "shortuser@sub"TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, sub_dom, "subdomu"); + assert_int_equal(ret, EEXIST); + ret = sss_ncache_check_upn(ncache, sub_dom, "subdomu@sub"TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, sub_dom, "upn"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_upn(ncache, sub_dom, "upn@upn.dom"); + assert_int_equal(ret, EEXIST); + + ret = check_user_in_ncache(ncache, sub_dom, "parentu"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_upn(ncache, sub_dom, "parentu@"TEST_DOM_NAME); + assert_int_equal(ret, EEXIST); + + + ret = check_group_in_ncache(ncache, sub_dom, "shortgroup"); + assert_int_equal(ret, EEXIST); + + ret = check_group_in_ncache(ncache, sub_dom, "parentg"); + assert_int_equal(ret, ENOENT); + + ret = check_group_in_ncache(ncache, sub_dom, "subdomg"); + assert_int_equal(ret, EEXIST); +} +static void expect_no_entries_in_dom(struct sss_nc_ctx *ncache, + struct sss_domain_info *dom2) +{ + int ret; + + ret = check_user_in_ncache(ncache, dom2, "shortuser"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_upn(ncache, dom2, "shortuser"TEST_DOM_NAME); + assert_int_equal(ret, ENOENT); + + ret = check_user_in_ncache(ncache, dom2, "parentu"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_upn(ncache, dom2, "parentu@"TEST_DOM_NAME); + assert_int_equal(ret, ENOENT); + + ret = check_user_in_ncache(ncache, dom2, "subdomu"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_upn(ncache, dom2, "subdomu@sub"TEST_DOM_NAME); + assert_int_equal(ret, ENOENT); + + ret = check_user_in_ncache(ncache, dom2, "upn"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_upn(ncache, dom2, "upn@upn.dom"); + assert_int_equal(ret, ENOENT); + + ret = check_group_in_ncache(ncache, dom2, "shortgroup"); + assert_int_equal(ret, ENOENT); + + ret = check_group_in_ncache(ncache, dom2, "parentg"); + assert_int_equal(ret, ENOENT); + + ret = check_group_in_ncache(ncache, dom2, "subdomg"); + assert_int_equal(ret, ENOENT); +} + +static void run_sss_ncache_short_name_in_domain(void **state, + bool use_default_domain_prefix) +{ + int ret; + struct test_state *ts; + struct tevent_context *ev; + struct sss_nc_ctx *ncache; + struct sss_test_ctx *tc; + struct sss_domain_info *dom; + struct sss_domain_info *dom2; + struct sss_domain_info *sub_dom; + + struct sss_test_conf_param params[] = { + { "filter_users", "shortuser, parentu@"TEST_DOM_NAME", " + "subdomu@sub"TEST_DOM_NAME", upn@upn.dom" }, + { "filter_groups", "shortgroup, parentg@"TEST_DOM_NAME", " + "subdomg@sub"TEST_DOM_NAME }, + { NULL, NULL }, + }; + + const char *sss_nss_filter_users[] = { params[0].value, NULL}; + const char *sss_nss_filter_groups[] = { params[1].value, NULL}; + + ts = talloc_get_type_abort(*state, struct test_state); + + ev = tevent_context_init(ts); + assert_non_null(ev); + + dom = talloc_zero(ts, struct sss_domain_info); + assert_non_null(dom); + dom->name = discard_const_p(char, TEST_DOM_NAME); + sss_domain_set_state(dom, DOM_ACTIVE); + + ts->nctx = mock_nctx(ts); + assert_non_null(ts->nctx); + + tc = create_dom_test_ctx(ts, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, TEST_ID_PROVIDER, params); + assert_non_null(tc); + + ret = confdb_add_param(tc->confdb, true, "config/domain/"TEST_DOM_NAME, + "filter_users", sss_nss_filter_users); + assert_int_equal(ret, EOK); + + ret = confdb_add_param(tc->confdb, true, "config/domain"TEST_DOM_NAME, + "filter_groups", sss_nss_filter_groups); + assert_int_equal(ret, EOK); + + ncache = ts->ctx; + ts->rctx = mock_rctx(ts, ev, dom, ts->nctx); + assert_non_null(ts->rctx); + if (use_default_domain_prefix) { + ts->rctx->default_domain = discard_const(TEST_DOM_NAME); + } + ts->rctx->cdb = tc->confdb; + + ret = sss_names_init(ts, tc->confdb, TEST_DOM_NAME, &dom->names); + assert_int_equal(ret, EOK); + + ret = sss_ncache_reset_repopulate_permanent(ts->rctx, ncache); + assert_int_equal(ret, EOK); + + /* Add another domain */ + dom2 = talloc_zero(ts, struct sss_domain_info); + assert_non_null(dom2); + dom2->name = discard_const_p(char, TEST_DOM_NAME"2"); + sss_domain_set_state(dom2, DOM_ACTIVE); + dom->next = dom2; + dom2->names = dom->names; + + expect_in_parent(ncache, dom); + expect_no_entries_in_dom(ncache, dom2); + + ret = sss_ncache_reset_repopulate_permanent(ts->rctx, ncache); + assert_int_equal(ret, EOK); + + expect_in_parent(ncache, dom); + expect_no_entries_in_dom(ncache, dom2); + + /* Add a sub domain */ + sub_dom = talloc_zero(ts, struct sss_domain_info); + assert_non_null(sub_dom); + sub_dom->name = discard_const_p(char, "sub"TEST_DOM_NAME); + sss_domain_set_state(sub_dom, DOM_ACTIVE); + sub_dom->parent = dom; + dom->subdomains = sub_dom; + sub_dom->names = dom->names; + + ret = sss_ncache_reset_repopulate_permanent(ts->rctx, ncache); + assert_int_equal(ret, EOK); + + expect_in_parent(ncache, dom); + expect_in_subdomain(ncache, sub_dom); + expect_no_entries_in_dom(ncache, dom2); +} + +static void test_sss_ncache_short_name_in_domain(void **state) +{ + run_sss_ncache_short_name_in_domain(state, false); +} + +static void test_sss_ncache_short_name_in_domain_with_prefix(void **state) +{ + run_sss_ncache_short_name_in_domain(state, true); +} + +static void test_sss_ncache_reset(void **state) +{ + errno_t ret; + struct test_state *ts; + struct sss_domain_info *dom; + + ts = talloc_get_type_abort(*state, struct test_state); + dom = talloc(ts, struct sss_domain_info); + assert_non_null(dom); + dom->case_sensitive = true; + + dom->name = discard_const_p(char, TEST_DOM_NAME); + + /* Set users */ + ret = sss_ncache_check_uid(ts->ctx, NULL, 123); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_set_uid(ts->ctx, false, NULL, 123); + assert_int_equal(ret, EOK); + ret = sss_ncache_check_uid(ts->ctx, NULL, 123); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_user(ts->ctx, dom, "foo"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_set_user(ts->ctx, false, dom, "foo"); + assert_int_equal(ret, EOK); + ret = sss_ncache_check_user(ts->ctx, dom, "foo"); + assert_int_equal(ret, EEXIST); + + /* Set groups */ + ret = sss_ncache_check_gid(ts->ctx, NULL, 456); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_set_gid(ts->ctx, false, NULL, 456); + assert_int_equal(ret, EOK); + ret = sss_ncache_check_gid(ts->ctx, NULL, 456); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_group(ts->ctx, dom, "bar"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_set_group(ts->ctx, false, dom, "bar"); + assert_int_equal(ret, EOK); + ret = sss_ncache_check_group(ts->ctx, dom, "bar"); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_reset_users(ts->ctx); + assert_int_equal(ret, EOK); + + /* Users are no longer negatively cached */ + ret = sss_ncache_check_user(ts->ctx, dom, "foo"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_uid(ts->ctx, NULL, 123); + assert_int_equal(ret, ENOENT); + + /* Groups still are */ + ret = sss_ncache_check_gid(ts->ctx, NULL, 456); + assert_int_equal(ret, EEXIST); + ret = sss_ncache_check_group(ts->ctx, dom, "bar"); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_reset_groups(ts->ctx); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_gid(ts->ctx, NULL, 456); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_group(ts->ctx, dom, "bar"); + assert_int_equal(ret, ENOENT); +} + +static void test_sss_ncache_locate_uid_gid_sid(void **state) +{ + uid_t uid; + gid_t gid; + const char *sid = "S-1-3-0-9999-9999-99"; + int ret; + struct test_state *ts; + struct sss_domain_info *dom; + struct sss_domain_info *dom2; + + ts = talloc_get_type_abort(*state, struct test_state); + + uid = getuid(); + gid = getgid(); + + dom = talloc(ts, struct sss_domain_info); + assert_non_null(dom); + dom->name = discard_const_p(char, TEST_DOM_NAME); + + dom2 = talloc(ts, struct sss_domain_info); + assert_non_null(dom2); + dom2->name = discard_const_p(char, TEST_DOM_NAME"2"); + + ret = sss_ncache_check_locate_gid(ts->ctx, dom, gid); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_locate_uid(ts->ctx, dom, uid); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_locate_sid(ts->ctx, dom, sid); + assert_int_equal(ret, ENOENT); + + ret = sss_ncache_set_locate_gid(ts->ctx, dom, gid); + assert_int_equal(ret, EOK); + ret = sss_ncache_set_locate_uid(ts->ctx, dom, uid); + assert_int_equal(ret, EOK); + ret = sss_ncache_set_locate_sid(ts->ctx, dom, sid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_check_locate_gid(ts->ctx, dom, gid); + assert_int_equal(ret, EEXIST); + ret = sss_ncache_check_locate_uid(ts->ctx, dom, uid); + assert_int_equal(ret, EEXIST); + ret = sss_ncache_check_locate_sid(ts->ctx, dom, sid); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_locate_gid(ts->ctx, dom2, gid); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_locate_uid(ts->ctx, dom2, uid); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_check_locate_sid(ts->ctx, dom2, sid); + assert_int_equal(ret, ENOENT); +} + +static void test_sss_ncache_domain_locate_type(void **state) +{ + int ret; + struct test_state *ts; + struct sss_domain_info *dom; + struct sss_domain_info *dom2; + + ts = talloc_get_type_abort(*state, struct test_state); + + dom = talloc(ts, struct sss_domain_info); + assert_non_null(dom); + dom->name = discard_const_p(char, TEST_DOM_NAME); + + dom2 = talloc(ts, struct sss_domain_info); + assert_non_null(dom2); + dom2->name = discard_const_p(char, TEST_DOM_NAME"2"); + + ret = sss_ncache_check_domain_locate_type(ts->ctx, dom, "foo"); + assert_int_equal(ret, ENOENT); + ret = sss_ncache_set_domain_locate_type(ts->ctx, dom, "foo"); + assert_int_equal(ret, EOK); + ret = sss_ncache_check_domain_locate_type(ts->ctx, dom, "foo"); + assert_int_equal(ret, EEXIST); + + ret = sss_ncache_check_domain_locate_type(ts->ctx, dom2, "foo"); + assert_int_equal(ret, ENOENT); +} + +int main(void) +{ + int rv; + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sss_ncache_init), + cmocka_unit_test_setup_teardown(test_sss_ncache_uid, setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_gid, setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_sid, setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_cert, setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_user, setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_group, setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_netgr, setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_service_name, setup, + teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_service_port, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_reset_permanent, setup, + teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_prepopulate, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_default_domain_suffix, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_reset_prepopulate, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_short_name_in_domain, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_short_name_in_domain_with_prefix, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_reset, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_locate_uid_gid_sid, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_domain_locate_type, + setup, teardown), + + /* user */ + cmocka_unit_test_setup_teardown(test_ncache_nocache_user, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_local_user, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_domain_user, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_both_user, + test_ncache_setup, + test_ncache_teardown), + /* uid */ + cmocka_unit_test_setup_teardown(test_ncache_nocache_uid, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_local_uid, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_domain_uid, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_both_uid, + test_ncache_setup, + test_ncache_teardown), + /* group */ + cmocka_unit_test_setup_teardown(test_ncache_nocache_group, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_local_group, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_domain_group, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_both_group, + test_ncache_setup, + test_ncache_teardown), + /* gid */ + cmocka_unit_test_setup_teardown(test_ncache_nocache_gid, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_local_gid, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_domain_gid, + test_ncache_setup, + test_ncache_teardown), + cmocka_unit_test_setup_teardown(test_ncache_both_gid, + test_ncache_setup, + test_ncache_teardown), + }; + + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + return rv; +} diff --git a/src/tests/cmocka/test_negcache_2.c b/src/tests/cmocka/test_negcache_2.c new file mode 100644 index 0000000..5b88708 --- /dev/null +++ b/src/tests/cmocka/test_negcache_2.c @@ -0,0 +1,749 @@ +/* + Authors: + Petr ト憩ch <pcech@redhat.com> + + Copyright (C) 2016 Red Hat + + 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/>. +*/ + +#define _GNU_SOURCE /* for `fgetpwent()` in musl libc */ +#include <stdio.h> +#include <sys/types.h> +#include <pwd.h> + +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> + +#include "tests/common.h" +#include "responder/common/negcache.h" +#include "responder/common/negcache_files.h" +#include "responder/common/responder.h" + +#define TIMEOUT 10000 + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_negcache_confdb.ldb" +#define TEST_DOM_NAME "test_domain.test" + +struct user_descriptor_t { + const char *name; + uid_t uid; +}; + +struct group_descriptor_t { + const char *name; + gid_t gid; +}; + +struct ncache_test_ctx { + struct sss_test_ctx *tctx; + struct sss_nc_ctx *ncache; + struct user_descriptor_t local_users[2]; + struct user_descriptor_t non_local_users[2]; + struct group_descriptor_t local_groups[2]; + struct group_descriptor_t non_local_groups[2]; +}; + +static void create_users(struct ncache_test_ctx *test_ctx) +{ + errno_t ret; + char *fqname; + struct sss_domain_info *domain = test_ctx->tctx->dom; + const struct user_descriptor_t *users = test_ctx->non_local_users; + const struct group_descriptor_t *groups = test_ctx->non_local_groups; + + for (int i = 0; i < 2; i++) { + fqname = sss_create_internal_fqname(test_ctx, + users[i].name, + domain->name); + assert_non_null(fqname); + + ret = sysdb_add_user(domain, users[i].name, users[i].uid, groups[i].gid, + fqname, NULL, "/bin/bash", domain->name, + NULL, 30, time(NULL)); + talloc_free(fqname); + assert_int_equal(ret, EOK); + } +} + +static void create_groups(struct ncache_test_ctx *test_ctx) +{ + errno_t ret; + char *fqname; + struct sss_domain_info *domain = test_ctx->tctx->dom; + const struct group_descriptor_t *groups = test_ctx->non_local_groups; + + for (int i = 0; i < 2; i++) { + fqname = sss_create_internal_fqname(test_ctx, + groups[i].name, + domain->name); + assert_non_null(fqname); + + ret = sysdb_add_group(domain, fqname, groups[i].gid, + NULL, 30, time(NULL)); + talloc_free(fqname); + assert_int_equal(ret, EOK); + } +} + +static void find_local_users(struct ncache_test_ctx *test_ctx) +{ + int i; + FILE *passwd_file; + const struct passwd *pwd; + + passwd_file = fopen("/etc/passwd", "r"); + assert_non_null(passwd_file); + + for (i = 0; i < 2; /*no-op*/) { + pwd = fgetpwent(passwd_file); + assert_non_null(pwd); + if (pwd->pw_uid == 0) { + /* skip root */ + continue; + } + test_ctx->local_users[i].uid = pwd->pw_uid; + test_ctx->local_users[i].name = talloc_strdup(test_ctx, pwd->pw_name); + assert_non_null(test_ctx->local_users[i].name); + ++i; + } + + fclose(passwd_file); +} + +static void find_local_groups(struct ncache_test_ctx *test_ctx) +{ + int i; + FILE *group_file; + const struct group *grp; + + group_file = fopen("/etc/group", "r"); + assert_non_null(group_file); + + for (i = 0; i < 2; /* no-op */) { + grp = fgetgrent(group_file); + assert_non_null(grp); + if (grp->gr_gid == 0) { + /* skip root */ + continue; + } + test_ctx->local_groups[i].gid = grp->gr_gid; + test_ctx->local_groups[i].name = talloc_strdup(test_ctx, grp->gr_name); + assert_non_null(test_ctx->local_groups[i].name); + ++i; + } + + fclose(group_file); +} + +static void find_non_local_users(struct ncache_test_ctx *test_ctx) +{ + int i; + int k; + uid_t uid; + char *name; + + for (i = 0, k = 1; (k < 100) && (i < 2); ++k) { + uid = 65534-k; + if (getpwuid(uid)) { + continue; + } + test_ctx->non_local_users[i].uid = uid; + ++i; + } + assert_int_equal(i, 2); + + for (i = 0, k = 0; (k < 100) && (i < 2); ++k) { + name = talloc_asprintf(test_ctx, "nctestuser%d", k); + if (getpwnam(name)) { + talloc_free(name); + continue; + } + test_ctx->non_local_users[i].name = name; + ++i; + } + assert_int_equal(i, 2); +} + +static void find_non_local_groups(struct ncache_test_ctx *test_ctx) +{ + int i = 0; + int k; + gid_t gid; + char *name; + + for (i = 0, k = 1; (k < 100) && (i < 2); ++k) { + gid = 65534-k; + if (getgrgid(gid)) { + continue; + } + test_ctx->non_local_groups[i].gid = gid; + ++i; + } + assert_int_equal(i, 2); + + for (i = 0, k = 0; (k < 100) && (i < 2); ++k) { + name = talloc_asprintf(test_ctx, "nctestgroup%d", k); + if (getgrnam(name)) { + talloc_free(name); + continue; + } + test_ctx->non_local_groups[i].name = name; + ++i; + } + assert_int_equal(i, 2); +} + +int test_ncache_setup(void **state) +{ + struct ncache_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct ncache_test_ctx); + assert_non_null(test_ctx); + + find_local_users(test_ctx); + find_local_groups(test_ctx); + find_non_local_users(test_ctx); + find_non_local_groups(test_ctx); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, "ipa", NULL); + assert_non_null(test_ctx->tctx); + + create_groups(test_ctx); + create_users(test_ctx); + + check_leaks_push(test_ctx); + + *state = (void *)test_ctx; + + return 0; +} + +int test_ncache_teardown(void **state) +{ + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + + assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + assert_true(leak_check_teardown()); + + return 0; +} + +static int set_user_in_ncache(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name) +{ + char *fqdn; + int ret; + + fqdn = sss_create_internal_fqname(ctx, name, dom->name); + ret = sss_ncache_set_user(ctx, permanent, dom, fqdn); + talloc_free(fqdn); + return ret; +} + +static int set_group_in_ncache(struct sss_nc_ctx *ctx, bool permanent, + struct sss_domain_info *dom, const char *name) +{ + char *fqdn; + int ret; + + fqdn = sss_create_internal_fqname(ctx, name, dom->name); + ret = sss_ncache_set_group(ctx, permanent, dom, fqdn); + talloc_free(fqdn); + return ret; +} + +static int check_user_in_ncache(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *name) +{ + char *fqdn; + int ret; + + fqdn = sss_create_internal_fqname(ctx, name, dom->name); + ret = sss_ncache_check_user(ctx, dom, fqdn); + talloc_free(fqdn); + return ret; +} + +static int check_group_in_ncache(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, + const char *name) +{ + char *fqdn; + int ret; + + fqdn = sss_create_internal_fqname(ctx, name, dom->name); + ret = sss_ncache_check_group(ctx, dom, fqdn); + talloc_free(fqdn); + return ret; +} + +/* user utils */ + +static void set_users(struct ncache_test_ctx *test_ctx) +{ + int ret; + + ret = set_user_in_ncache(test_ctx->ncache, false, test_ctx->tctx->dom, + test_ctx->non_local_users[0].name); + assert_int_equal(ret, EOK); + + ret = set_user_in_ncache(test_ctx->ncache, false, test_ctx->tctx->dom, + test_ctx->local_users[0].name); + assert_int_equal(ret, EOK); +} + +static void check_users(struct ncache_test_ctx *test_ctx, + int case_a, int case_b, int case_c, int case_d) +{ + int ret; + + ret = check_user_in_ncache(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->non_local_users[0].name); + assert_int_equal(ret, case_a); + + ret = check_user_in_ncache(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->non_local_users[1].name); + assert_int_equal(ret, case_b); + + ret = check_user_in_ncache(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->local_users[0].name); + assert_int_equal(ret, case_c); + + ret = check_user_in_ncache(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->local_users[1].name); + assert_int_equal(ret, case_d); +} + +/* user tests */ + +void test_ncache_nocache_user(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, 0, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_users(test_ctx); + + check_users(test_ctx, ENOENT, ENOENT, ENOENT, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_local_user(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, 0, TIMEOUT, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_users(test_ctx); + + check_users(test_ctx, ENOENT, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_domain_user(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, TIMEOUT, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_users(test_ctx); + + check_users(test_ctx, EEXIST, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_both_user(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, TIMEOUT, TIMEOUT, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_users(test_ctx); + + check_users(test_ctx, EEXIST, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +/* uid utils */ + +static void set_uids(struct ncache_test_ctx *test_ctx) +{ + int ret; + + ret = sss_ncache_set_uid(test_ctx->ncache, false, test_ctx->tctx->dom, + test_ctx->non_local_users[0].uid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_set_uid(test_ctx->ncache, false, test_ctx->tctx->dom, + test_ctx->local_users[0].uid); + assert_int_equal(ret, EOK); +} + +static void check_uids(struct ncache_test_ctx *test_ctx, + int case_a, int case_b, int case_c, int case_d) +{ + int ret; + + ret = sss_ncache_check_uid(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->non_local_users[0].uid); + assert_int_equal(ret, case_a); + + ret = sss_ncache_check_uid(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->non_local_users[1].uid); + assert_int_equal(ret, case_b); + + ret = sss_ncache_check_uid(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->local_users[0].uid); + assert_int_equal(ret, case_c); + + ret = sss_ncache_check_uid(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->local_users[1].uid); + assert_int_equal(ret, case_d); +} + +/* uid tests */ + +void test_ncache_nocache_uid(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, 0, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_uids(test_ctx); + + check_uids(test_ctx, ENOENT, ENOENT, ENOENT, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_local_uid(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, 0, TIMEOUT, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_uids(test_ctx); + + check_uids(test_ctx, ENOENT, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_domain_uid(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, TIMEOUT, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_uids(test_ctx); + + check_uids(test_ctx, EEXIST, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_both_uid(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, TIMEOUT, TIMEOUT, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_uids(test_ctx); + + check_uids(test_ctx, EEXIST, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +/* group utils */ + +static void set_groups(struct ncache_test_ctx *test_ctx) +{ + int ret; + + ret = set_group_in_ncache(test_ctx->ncache, false, test_ctx->tctx->dom, + test_ctx->non_local_groups[0].name); + assert_int_equal(ret, EOK); + + ret = set_group_in_ncache(test_ctx->ncache, false, test_ctx->tctx->dom, + test_ctx->local_groups[0].name); + assert_int_equal(ret, EOK); +} + +static void check_groups(struct ncache_test_ctx *test_ctx, + int case_a, int case_b, int case_c, int case_d) +{ + int ret; + + ret = check_group_in_ncache(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->non_local_groups[0].name); + assert_int_equal(ret, case_a); + + ret = check_group_in_ncache(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->non_local_groups[1].name); + assert_int_equal(ret, case_b); + + ret = check_group_in_ncache(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->local_groups[0].name); + assert_int_equal(ret, case_c); + + ret = check_group_in_ncache(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->local_groups[1].name); + assert_int_equal(ret, case_d); +} + +/* group tests */ + +void test_ncache_nocache_group(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, 0, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_groups(test_ctx); + + check_groups(test_ctx, ENOENT, ENOENT, ENOENT, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_local_group(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, 0, TIMEOUT, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_groups(test_ctx); + + check_groups(test_ctx, ENOENT, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_domain_group(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, TIMEOUT, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_groups(test_ctx); + + check_groups(test_ctx, EEXIST, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_both_group(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, TIMEOUT, TIMEOUT, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_groups(test_ctx); + + check_groups(test_ctx, EEXIST, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +/* gid utils */ + +static void set_gids(struct ncache_test_ctx *test_ctx) +{ + int ret; + + ret = sss_ncache_set_gid(test_ctx->ncache, false, test_ctx->tctx->dom, + test_ctx->non_local_groups[0].gid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_set_gid(test_ctx->ncache, false, test_ctx->tctx->dom, + test_ctx->local_groups[0].gid); + assert_int_equal(ret, EOK); +} + +static void check_gids(struct ncache_test_ctx *test_ctx, + int case_a, int case_b, int case_c, int case_d) +{ + int ret; + + ret = sss_ncache_check_gid(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->non_local_groups[0].gid); + assert_int_equal(ret, case_a); + + ret = sss_ncache_check_gid(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->non_local_groups[1].gid); + assert_int_equal(ret, case_b); + + ret = sss_ncache_check_gid(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->local_groups[0].gid); + assert_int_equal(ret, case_c); + + ret = sss_ncache_check_gid(test_ctx->ncache, test_ctx->tctx->dom, + test_ctx->local_groups[1].gid); + assert_int_equal(ret, case_d); +} + +/* uid tests */ + +void test_ncache_nocache_gid(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, 0, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_gids(test_ctx); + + check_gids(test_ctx, ENOENT, ENOENT, ENOENT, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_local_gid(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, 0, TIMEOUT, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_gids(test_ctx); + + check_gids(test_ctx, ENOENT, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_domain_gid(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, TIMEOUT, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_gids(test_ctx); + + check_gids(test_ctx, EEXIST, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} + +void test_ncache_both_gid(void **state) +{ + errno_t ret; + struct ncache_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct ncache_test_ctx); + assert_non_null(test_ctx); + + ret = sss_ncache_init(test_ctx, TIMEOUT, TIMEOUT, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + set_gids(test_ctx); + + check_gids(test_ctx, EEXIST, ENOENT, EEXIST, ENOENT); + + talloc_zfree(test_ctx->ncache); +} diff --git a/src/tests/cmocka/test_nested_groups.c b/src/tests/cmocka/test_nested_groups.c new file mode 100644 index 0000000..25fd3de --- /dev/null +++ b/src/tests/cmocka/test_nested_groups.c @@ -0,0 +1,1416 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2013 Red Hat + + 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 <talloc.h> +#include <tevent.h> +#include <ldb.h> +#include <errno.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_sdap.h" +#include "tests/cmocka/common_mock_be.h" +#include "tests/cmocka/common_mock_sysdb_objects.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap.h" +#include "providers/ldap/sdap_idmap.h" +#include "providers/ldap/sdap_async_private.h" +#include "providers/ldap/ldap_opts.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_ldap_nested_groups_conf.ldb" +#define TEST_DOM_NAME "ldap_nested_groups_test" +#define TEST_ID_PROVIDER "ldap" +#define TEST_EXT_MEMBER "extMember" + +bool _dp_target_enabled(struct data_provider *provider, + const char *module_name, + ...) +{ + return true; +} + +#define new_test(test) \ + cmocka_unit_test_setup_teardown(nested_groups_test_ ## test, \ + nested_groups_test_setup, \ + nested_groups_test_teardown) + +/* put users and groups under the same container so we can easily run the + * same tests cases for several search base scenarios */ +#define OBJECT_BASE_DN "cn=objects,dc=test,dc=com" +#define GROUP_BASE_DN "cn=groups," OBJECT_BASE_DN +#define USER_BASE_DN "cn=users," OBJECT_BASE_DN +#define EXCLUDE_BASE_DN "cn=exclude," USER_BASE_DN +#define BAD_BASE_DN "cn=bad," USER_BASE_DN + +struct nested_groups_test_ctx { + struct sss_test_ctx *tctx; + + struct be_ctx *be_ctx; + struct sdap_options *sdap_opts; + struct sdap_handle *sdap_handle; + struct sdap_domain *sdap_domain; + struct sdap_idmap_ctx *idmap_ctx; + struct sdap_id_ctx *sdap_id_ctx; + hash_table_t *missing_external; + + struct sysdb_attrs **users; + struct sysdb_attrs **groups; + unsigned long num_users; + unsigned long num_groups; + + /* External members tests */ + struct sdap_ext_member_ctx *ext_ctx; + enum sysdb_member_type ext_member_type; + struct sss_domain_info *ext_dom; + struct sysdb_attrs *ext_member; +}; + +errno_t krb5_try_kdcip(struct confdb_ctx *cdb, + const char *conf_path, + struct dp_option *opts, + int opt_id) +{ + return EOK; +} + +/* Both arrays must have the same length! */ +static void compare_sysdb_string_array_noorder(struct sysdb_attrs **sysdb_array, + const char **string_array, + size_t len) +{ + int i, ii; + errno_t ret; + const char *name; + + /* Check the returned groups. The order is irrelevant. */ + for (i = 0; i < len; i++) { + ret = sysdb_attrs_get_string(sysdb_array[i], SYSDB_NAME, &name); + assert_int_equal(ret, ERR_OK); + + for (ii = 0; ii < len; ii++) { + if (string_array[ii] == NULL) { + continue; + } + if (strcmp(name, string_array[ii]) == 0) { + string_array[ii] = NULL; + break; + } + } + } + + for (i = 0; i < len; i++) { + assert_null(string_array[i]); + } +} + +static void nested_groups_test_done(struct tevent_req *req) +{ + struct nested_groups_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct nested_groups_test_ctx); + + ctx->tctx->error = sdap_nested_group_recv(ctx, req, + &ctx->num_users, &ctx->users, + &ctx->num_groups, &ctx->groups, + &ctx->missing_external); + talloc_zfree(req); + + ctx->tctx->done = true; +} + +static void nested_groups_test_one_group_no_members(void **state) +{ + struct nested_groups_test_ctx *test_ctx = NULL; + struct sysdb_attrs *rootgroup = NULL; + struct tevent_req *req = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct nested_groups_test_ctx); + + rootgroup = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, 1000, + "rootgroup", NULL); + + /* mock return values */ + sss_will_return_always(sdap_has_deref_support, false); + + /* run test, check for memory leaks */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + req = sdap_nested_group_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->sdap_domain, test_ctx->sdap_opts, + test_ctx->sdap_handle, rootgroup); + assert_non_null(req); + tevent_req_set_callback(req, nested_groups_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx) == true); + talloc_zfree(req_mem_ctx); + /* check return code */ + assert_int_equal(ret, ERR_OK); + + /* check generated values */ + assert_int_equal(test_ctx->num_users, 0); + assert_null(test_ctx->users); + + assert_int_equal(test_ctx->num_groups, 1); + assert_non_null(test_ctx->groups); + assert_true(rootgroup == test_ctx->groups[0]); +} + +static void nested_groups_test_one_group_unique_members(void **state) +{ + struct nested_groups_test_ctx *test_ctx = NULL; + struct sysdb_attrs *rootgroup = NULL; + struct tevent_req *req = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + errno_t ret; + const char *users[] = { "cn=user1,"USER_BASE_DN, + "cn=user2,"USER_BASE_DN, + NULL }; + const struct sysdb_attrs *user1_reply[2] = { NULL }; + const struct sysdb_attrs *user2_reply[2] = { NULL }; + const char * expected[] = { "user1", + "user2" }; + + + test_ctx = talloc_get_type_abort(*state, struct nested_groups_test_ctx); + + /* mock return values */ + rootgroup = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, 1000, + "rootgroup", users); + + user1_reply[0] = mock_sysdb_user(test_ctx, USER_BASE_DN, 2001, "user1"); + assert_non_null(user1_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, user1_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + user2_reply[0] = mock_sysdb_user(test_ctx, USER_BASE_DN, 2002, "user2"); + assert_non_null(user2_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, user2_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + sss_will_return_always(sdap_has_deref_support, false); + + /* run test, check for memory leaks */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + req = sdap_nested_group_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->sdap_domain, test_ctx->sdap_opts, + test_ctx->sdap_handle, rootgroup); + assert_non_null(req); + tevent_req_set_callback(req, nested_groups_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx) == true); + talloc_zfree(req_mem_ctx); + + /* check return code */ + assert_int_equal(ret, ERR_OK); + + /* Check the users */ + assert_int_equal(test_ctx->num_users, N_ELEMENTS(expected)); + assert_int_equal(test_ctx->num_groups, 1); + + compare_sysdb_string_array_noorder(test_ctx->users, + expected, N_ELEMENTS(expected)); +} + +static void nested_groups_test_one_group_unique_members_one_ignored(void **state) +{ + struct nested_groups_test_ctx *test_ctx = NULL; + struct sdap_search_base **ignore; + struct sysdb_attrs *rootgroup = NULL; + struct tevent_req *req = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + errno_t ret; + const char *users[] = { "cn=user1," USER_BASE_DN, + "cn=user2," EXCLUDE_BASE_DN, + NULL }; + const struct sysdb_attrs *user1_reply[2] = { NULL }; + const char * expected[] = { "user1" }; + + + test_ctx = talloc_get_type_abort(*state, struct nested_groups_test_ctx); + + /* mock return values */ + rootgroup = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, 1000, + "rootgroup", users); + + /* Set the exclude bases */ + ignore = talloc_zero_array(test_ctx, struct sdap_search_base *, 3); + assert_non_null(ignore); + + ignore[0] = talloc_zero(ignore, struct sdap_search_base); + assert_non_null(ignore[0]); + ignore[0]->basedn = BAD_BASE_DN; + ignore[0]->ldb_basedn = ldb_dn_new(ignore[0], + sysdb_ctx_get_ldb(test_ctx->tctx->sysdb), + ignore[0]->basedn); + assert_non_null(ignore[0]->ldb_basedn); + + ignore[1] = talloc_zero(ignore, struct sdap_search_base); + assert_non_null(ignore[1]); + ignore[1]->basedn = EXCLUDE_BASE_DN; + ignore[1]->ldb_basedn = ldb_dn_new(ignore[1], + sysdb_ctx_get_ldb(test_ctx->tctx->sysdb), + ignore[1]->basedn); + assert_non_null(ignore[1]->ldb_basedn); + + test_ctx->sdap_domain->ignore_user_search_bases = ignore; + + user1_reply[0] = mock_sysdb_user(test_ctx, USER_BASE_DN, 2001, "user1"); + assert_non_null(user1_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, user1_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + sss_will_return_always(sdap_has_deref_support, false); + + /* run test, check for memory leaks */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + req = sdap_nested_group_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->sdap_domain, test_ctx->sdap_opts, + test_ctx->sdap_handle, rootgroup); + assert_non_null(req); + tevent_req_set_callback(req, nested_groups_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx)); + talloc_zfree(req_mem_ctx); + + /* check return code */ + assert_int_equal(ret, ERR_OK); + + /* Check the users */ + assert_int_equal(test_ctx->num_users, N_ELEMENTS(expected)); + assert_int_equal(test_ctx->num_groups, 1); + + compare_sysdb_string_array_noorder(test_ctx->users, + expected, N_ELEMENTS(expected)); +} + +static void nested_groups_test_one_group_dup_users(void **state) +{ + struct nested_groups_test_ctx *test_ctx = NULL; + struct sysdb_attrs *rootgroup = NULL; + struct tevent_req *req = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + errno_t ret; + const char *name; + const char *users[] = { "cn=user1,"USER_BASE_DN, + "cn=user1,"USER_BASE_DN, + NULL }; + const struct sysdb_attrs *user1_reply[2] = { NULL }; + const struct sysdb_attrs *user2_reply[2] = { NULL }; + + test_ctx = talloc_get_type_abort(*state, struct nested_groups_test_ctx); + + /* mock return values */ + rootgroup = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, 1000, + "rootgroup", users); + + user1_reply[0] = mock_sysdb_user(test_ctx, USER_BASE_DN, 2001, "user1"); + assert_non_null(user1_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, user1_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + user2_reply[0] = mock_sysdb_user(test_ctx, USER_BASE_DN, 2001, "user1"); + assert_non_null(user2_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, user2_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + sss_will_return_always(sdap_has_deref_support, false); + + /* run test, check for memory leaks */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + req = sdap_nested_group_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->sdap_domain, test_ctx->sdap_opts, + test_ctx->sdap_handle, rootgroup); + assert_non_null(req); + tevent_req_set_callback(req, nested_groups_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx) == true); + talloc_zfree(req_mem_ctx); + + /* check return code */ + assert_int_equal(ret, ERR_OK); + + /* Check the users */ + assert_int_equal(test_ctx->num_users, 1); + assert_int_equal(test_ctx->num_groups, 1); + + ret = sysdb_attrs_get_string(test_ctx->users[0], SYSDB_NAME, &name); + assert_int_equal(ret, ERR_OK); + assert_string_equal(name, "user1"); +} + +static void nested_groups_test_one_group_unique_group_members(void **state) +{ + struct nested_groups_test_ctx *test_ctx = NULL; + struct sysdb_attrs *rootgroup = NULL; + struct tevent_req *req = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + errno_t ret; + const char *groups[] = { "cn=emptygroup1,"GROUP_BASE_DN, + "cn=emptygroup2,"GROUP_BASE_DN, + NULL }; + const struct sysdb_attrs *group1_reply[2] = { NULL }; + const struct sysdb_attrs *group2_reply[2] = { NULL }; + const char * expected[] = { "rootgroup", + "emptygroup1", + "emptygroup2" }; + + test_ctx = talloc_get_type_abort(*state, struct nested_groups_test_ctx); + + /* mock return values */ + rootgroup = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, 1000, + "rootgroup", groups); + + group1_reply[0] = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, + 1001, "emptygroup1", NULL); + assert_non_null(group1_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, group1_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + group2_reply[0] = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, + 1002, "emptygroup2", NULL); + assert_non_null(group2_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, group2_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + sss_will_return_always(sdap_has_deref_support, false); + + /* run test, check for memory leaks */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + req = sdap_nested_group_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->sdap_domain, test_ctx->sdap_opts, + test_ctx->sdap_handle, rootgroup); + assert_non_null(req); + tevent_req_set_callback(req, nested_groups_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx) == true); + talloc_zfree(req_mem_ctx); + + /* check return code */ + assert_int_equal(ret, ERR_OK); + + /* Check the users */ + assert_int_equal(test_ctx->num_users, 0); + assert_int_equal(test_ctx->num_groups, N_ELEMENTS(expected)); + + compare_sysdb_string_array_noorder(test_ctx->groups, + expected, N_ELEMENTS(expected)); +} + +static void nested_groups_test_one_group_dup_group_members(void **state) +{ + struct nested_groups_test_ctx *test_ctx = NULL; + struct sysdb_attrs *rootgroup = NULL; + struct tevent_req *req = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + errno_t ret; + const char *groups[] = { "cn=emptygroup1,"GROUP_BASE_DN, + "cn=emptygroup1,"GROUP_BASE_DN, + NULL }; + const struct sysdb_attrs *group1_reply[2] = { NULL }; + const struct sysdb_attrs *group2_reply[2] = { NULL }; + const char * expected[] = { "rootgroup", + "emptygroup1" }; + + test_ctx = talloc_get_type_abort(*state, struct nested_groups_test_ctx); + + /* mock return values */ + rootgroup = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, 1000, + "rootgroup", groups); + + group1_reply[0] = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, + 1001, "emptygroup1", NULL); + assert_non_null(group1_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, group1_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + group2_reply[0] = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, + 1001, "emptygroup1", NULL); + assert_non_null(group2_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, group2_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + sss_will_return_always(sdap_has_deref_support, false); + + /* run test, check for memory leaks */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + req = sdap_nested_group_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->sdap_domain, test_ctx->sdap_opts, + test_ctx->sdap_handle, rootgroup); + assert_non_null(req); + tevent_req_set_callback(req, nested_groups_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx) == true); + talloc_zfree(req_mem_ctx); + + /* check return code */ + assert_int_equal(ret, ERR_OK); + + assert_int_equal(test_ctx->num_users, 0); + assert_int_equal(test_ctx->num_groups, N_ELEMENTS(expected)); + + compare_sysdb_string_array_noorder(test_ctx->groups, + expected, N_ELEMENTS(expected)); +} + +static void nested_groups_test_nested_chain(void **state) +{ + struct nested_groups_test_ctx *test_ctx = NULL; + struct tevent_req *req = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + errno_t ret; + const char *rootgroup_members[] = { "cn=user1,"USER_BASE_DN, + "cn=group1,"GROUP_BASE_DN, + NULL }; + const char *group1_members[] = { "cn=user2,"USER_BASE_DN, + "cn=group2,"GROUP_BASE_DN, + NULL }; + const char *group2_members[] = { "cn=user3,"USER_BASE_DN, + NULL }; + struct sysdb_attrs *rootgroup; + const struct sysdb_attrs *user1_reply[2] = { NULL }; + const struct sysdb_attrs *group1_reply[2] = { NULL }; + const struct sysdb_attrs *user2_reply[2] = { NULL }; + const struct sysdb_attrs *group2_reply[2] = { NULL }; + const struct sysdb_attrs *user3_reply[2] = { NULL }; + const char *expected_groups[] = { "rootgroup", "group1", "group2" }; + const char *expected_users[] = { "user1", "user2", "user3" }; + + test_ctx = talloc_get_type_abort(*state, struct nested_groups_test_ctx); + + /* mock return values */ + rootgroup = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, 1000, + "rootgroup", rootgroup_members); + assert_non_null(rootgroup); + + user1_reply[0] = mock_sysdb_user(test_ctx, USER_BASE_DN, 2001, "user1"); + assert_non_null(user1_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, user1_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + group1_reply[0] = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, + 1001, "group1", + group1_members); + assert_non_null(group1_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, group1_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + user2_reply[0] = mock_sysdb_user(test_ctx, USER_BASE_DN, 2002, "user2"); + assert_non_null(user2_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, user2_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + group2_reply[0] = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, + 1002, "group2", + group2_members); + assert_non_null(group2_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, group2_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + user3_reply[0] = mock_sysdb_user(test_ctx, USER_BASE_DN, 2003, "user3"); + assert_non_null(user3_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, user3_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + sss_will_return_always(sdap_has_deref_support, false); + + /* run test, check for memory leaks */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + req = sdap_nested_group_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->sdap_domain, test_ctx->sdap_opts, + test_ctx->sdap_handle, rootgroup); + assert_non_null(req); + tevent_req_set_callback(req, nested_groups_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx) == true); + talloc_zfree(req_mem_ctx); + + /* check return code */ + assert_int_equal(ret, ERR_OK); + + /* Check the users */ + assert_int_equal(test_ctx->num_users, N_ELEMENTS(expected_users)); + assert_int_equal(test_ctx->num_groups, N_ELEMENTS(expected_groups)); + + compare_sysdb_string_array_noorder(test_ctx->groups, + expected_groups, + N_ELEMENTS(expected_groups)); + compare_sysdb_string_array_noorder(test_ctx->users, + expected_users, + N_ELEMENTS(expected_users)); +} + +static void nested_groups_test_nested_chain_with_error(void **state) +{ + struct nested_groups_test_ctx *test_ctx = NULL; + struct tevent_req *req = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + errno_t ret; + const char *rootgroup_members[] = { "cn=group1,"GROUP_BASE_DN, + NULL }; + const char *group1_members[] = { "cn=group2,"GROUP_BASE_DN, + NULL }; + const char *group2_members[] = { "cn=user1,"USER_BASE_DN, + NULL }; + struct sysdb_attrs *rootgroup; + const struct sysdb_attrs *user_reply[2] = { NULL }; + const struct sysdb_attrs *group1_reply[2] = { NULL }; + const struct sysdb_attrs *group2_reply[2] = { NULL }; + + test_ctx = talloc_get_type_abort(*state, struct nested_groups_test_ctx); + + /* mock return values */ + rootgroup = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, 1000, + "rootgroup", rootgroup_members); + assert_non_null(rootgroup); + + group1_reply[0] = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, + 1001, "group1", + group1_members); + assert_non_null(group1_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, group1_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + group2_reply[0] = mock_sysdb_group_rfc2307bis(test_ctx, GROUP_BASE_DN, + 1002, "group2", + group2_members); + assert_non_null(group2_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, group2_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + user_reply[0] = mock_sysdb_user(test_ctx, USER_BASE_DN, 2001, "user1"); + assert_non_null(user_reply[0]); + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, user_reply); + will_return(sdap_get_generic_recv, EIO); + + sss_will_return_always(sdap_has_deref_support, false); + + /* run test, check for memory leaks */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + req = sdap_nested_group_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->sdap_domain, test_ctx->sdap_opts, + test_ctx->sdap_handle, rootgroup); + assert_non_null(req); + tevent_req_set_callback(req, nested_groups_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx) == true); + talloc_zfree(req_mem_ctx); + + /* check return code */ + assert_int_equal(ret, EIO); +} + +static int nested_groups_test_setup(void **state) +{ + errno_t ret; + struct nested_groups_test_ctx *test_ctx = NULL; + static struct sss_test_conf_param params[] = { + { "ldap_schema", "rfc2307bis" }, /* enable nested groups */ + { "ldap_search_base", OBJECT_BASE_DN }, + { "ldap_user_search_base", USER_BASE_DN }, + { "ldap_group_search_base", GROUP_BASE_DN }, + { NULL, NULL } + }; + + test_ctx = talloc_zero(NULL, struct nested_groups_test_ctx); + assert_non_null(test_ctx); + *state = test_ctx; + + /* initialize domain */ + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, + TEST_ID_PROVIDER, params); + assert_non_null(test_ctx->tctx); + + /* mock SDAP */ + test_ctx->sdap_opts = mock_sdap_options_ldap(test_ctx, + test_ctx->tctx->dom, + test_ctx->tctx->confdb, + test_ctx->tctx->conf_dom_path); + assert_non_null(test_ctx->sdap_opts); + test_ctx->sdap_domain = test_ctx->sdap_opts->sdom; + test_ctx->sdap_handle = mock_sdap_handle(test_ctx); + assert_non_null(test_ctx->sdap_handle); + + test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx); + assert_non_null(test_ctx->be_ctx); + + test_ctx->sdap_id_ctx = mock_sdap_id_ctx(test_ctx, + test_ctx->be_ctx, + test_ctx->sdap_opts); + assert_non_null(test_ctx->sdap_id_ctx); + + ret = sdap_idmap_init(test_ctx, test_ctx->sdap_id_ctx, &test_ctx->idmap_ctx); + assert_int_equal(ret, EOK); + test_ctx->sdap_opts->idmap_ctx = test_ctx->idmap_ctx; + + test_ctx->ext_ctx = talloc_zero(test_ctx, struct sdap_ext_member_ctx); + assert_non_null(test_ctx->ext_ctx); + + return 0; +} + +static int nested_groups_test_teardown(void **state) +{ + talloc_zfree(*state); + return 0; +} + +struct test_ext_pvt { + struct sss_domain_info *dom_head; +}; + +struct test_ext_member { + const char *sid; + const char *short_name; + id_t id; + enum sysdb_member_type member_type; +} test_ext_member_table[] = { + { "S-1-5-21-3623811015-3361044348-30300820-10001", + "ext_user10001", 10001, SYSDB_MEMBER_USER }, + { "S-1-5-21-3623811015-3361044348-30300820-20001", + "ext_group20001", 10001, SYSDB_MEMBER_GROUP }, + { NULL, NULL, 0, 0 }, +}; + +struct test_resolve_ext_state { + struct sss_domain_info *dom; + enum sysdb_member_type member_type; + struct sysdb_attrs *member; +}; + +static errno_t test_resolve_ext_save_obj(TALLOC_CTX *mem_ctx, + struct sss_domain_info *dom, + const char *name, + id_t id, + enum sysdb_member_type member_type, + struct sysdb_attrs **_member); + +struct tevent_req *test_resolve_ext_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *ext_member, + void *pvt) +{ + struct tevent_req *req; + struct test_resolve_ext_state *state; + errno_t ret; + struct test_ext_pvt *test_pvt = talloc_get_type(pvt, struct test_ext_pvt); + struct sysdb_attrs *member; + + req = tevent_req_create(mem_ctx, &state, struct test_resolve_ext_state); + if (req == NULL) { + return NULL; + } + + for (size_t i = 0; test_ext_member_table[i].sid; i++) { + if (strcmp(ext_member, test_ext_member_table[i].sid) == 0) { + ret = test_resolve_ext_save_obj(state, test_pvt->dom_head, + test_ext_member_table[i].short_name, + test_ext_member_table[i].id, + test_ext_member_table[i].member_type, + &member); + if (ret != EOK) { + goto immediate; + } + + state->dom = test_pvt->dom_head; + state->member_type = test_ext_member_table[i].member_type; + state->member = talloc_steal(state, member); + + ret = EOK; + goto immediate; + } + } + + ret = ENOENT; + +immediate: + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } + tevent_req_post(req, ev); + return req; +} + +static errno_t test_resolve_ext_save_obj(TALLOC_CTX *mem_ctx, + struct sss_domain_info *dom, + const char *name, + id_t id, + enum sysdb_member_type member_type, + struct sysdb_attrs **_member) +{ + errno_t ret; + struct ldb_result *res; + char *home; + struct sysdb_attrs **members; + TALLOC_CTX *tmp_ctx; + char *fqdn; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + fqdn = sss_create_internal_fqname(tmp_ctx, name, dom->name); + if (fqdn == NULL) { + ret = ENOMEM; + goto done; + } + + if (member_type == SYSDB_MEMBER_USER) { + home = talloc_asprintf(tmp_ctx, "/home/%s", name); + if (home == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_store_user(dom, fqdn, "*", id, id, + name, home, "/bin/bash", NULL, NULL, + NULL, 1000, time(NULL)); + if (ret != EOK) { + goto done; + } + + ret = sysdb_getpwnam(tmp_ctx, dom, fqdn, &res); + if (ret != EOK) { + goto done; + } + } else if (member_type == SYSDB_MEMBER_GROUP) { + ret = sysdb_store_group(dom, fqdn, id, NULL, 1000, time(NULL)); + if (ret != EOK) { + goto done; + } + + ret = sysdb_getgrnam(tmp_ctx, dom, fqdn, &res); + if (ret != EOK) { + goto done; + } + } else { + ret = EINVAL; + goto done; + } + + ret = sysdb_msg2attrs(tmp_ctx, 1, res->msgs, &members); + if (ret != EOK) { + goto done; + } + + *_member = talloc_steal(mem_ctx, members[0]); + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t test_resolve_ext_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + enum sysdb_member_type *_member_type, + struct sss_domain_info **_dom, + struct sysdb_attrs **_member) +{ + struct test_resolve_ext_state *state = tevent_req_data(req, + struct test_resolve_ext_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_member_type != NULL) { + *_member_type = state->member_type; + } + + if (_dom) { + *_dom = state->dom; + } + + if (_member != NULL) { + *_member = talloc_steal(mem_ctx, state->member); + } + + return EOK; +} + +static int nested_group_external_member_setup(void **state) +{ + struct nested_groups_test_ctx *test_ctx; + struct test_ext_pvt *ext_pvt; + int ret; + + ret = nested_groups_test_setup((void **) &test_ctx); + assert_int_equal(ret, 0); + + ext_pvt = talloc_zero(test_ctx->ext_ctx, struct test_ext_pvt); + assert_non_null(ext_pvt); + ext_pvt->dom_head = test_ctx->tctx->dom; + + test_ctx->ext_ctx->ext_member_resolve_send = test_resolve_ext_send; + test_ctx->ext_ctx->ext_member_resolve_recv = test_resolve_ext_recv; + test_ctx->ext_ctx->pvt = ext_pvt; + + *state = test_ctx; + return 0; +} + +static int nested_group_external_member_teardown(void **state) +{ + struct nested_groups_test_ctx *test_ctx = talloc_get_type(*state, + struct nested_groups_test_ctx); + errno_t ret; + char *fqdn; + int i; + + ret = sysdb_delete_group(test_ctx->tctx->dom, "rootgroup", 0); + if (ret != EOK && ret != ENOENT) { + return 1; + } + + for (i = 0; test_ext_member_table[i].sid != NULL; i++) { + fqdn = sss_create_internal_fqname(test_ctx, + test_ext_member_table[i].short_name, + test_ctx->tctx->dom->name); + if (fqdn == NULL) { + return 1; + } + + switch (test_ext_member_table[i].member_type) { + case SYSDB_MEMBER_USER: + ret = sysdb_delete_user(test_ctx->tctx->dom, + fqdn, 0); + break; + + case SYSDB_MEMBER_GROUP: + ret = sysdb_delete_group(test_ctx->tctx->dom, + fqdn, 0); + break; + + default: + continue; + } + + talloc_zfree(fqdn); + + if (ret != EOK && ret != ENOENT) { + return 1; + } + } + + talloc_free(test_ctx->ext_ctx); + return nested_groups_test_teardown(*state); +} + +static void nested_external_done(struct tevent_req *req) +{ + struct nested_groups_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct nested_groups_test_ctx); + + ctx->tctx->error = sdap_nested_group_lookup_external_recv(ctx, req); + talloc_zfree(req); + + ctx->tctx->done = true; +} + +static struct sysdb_attrs * +mock_group_with_ext_members(struct nested_groups_test_ctx *test_ctx, + const char *name, + gid_t gid, + const char *ext_members[]) +{ + struct sysdb_attrs *ext_group = NULL; + const struct sysdb_attrs **ext_group_reply; + int i; + errno_t ret; + + ext_group_reply = talloc_zero_array(test_ctx, + const struct sysdb_attrs *, + 2); + if (ext_group_reply == NULL) { + return NULL; + } + + ext_group = mock_sysdb_object(ext_group_reply, GROUP_BASE_DN, name, + SYSDB_GIDNUM, gid); + if (ext_group == NULL) { + talloc_free(ext_group_reply); + return NULL; + } + + for (i = 0; ext_members[i] != NULL; i++) { + ret = sysdb_attrs_add_string( + ext_group, + test_ctx->sdap_opts->group_map[SDAP_AT_GROUP_EXT_MEMBER].sys_name, + ext_members[i]); + if (ret != EOK) { + talloc_free(ext_group_reply); + return NULL; + } + } + + ext_group_reply[0] = ext_group; + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, ext_group_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + return ext_group; +} + +static errno_t +nested_group_test_save_group(struct nested_groups_test_ctx *test_ctx, + struct sysdb_attrs *ldap_attrs, + struct group *gr) +{ + errno_t ret; + struct sysdb_attrs *sysdb_grattrs = NULL; + const char *s; + char *fqdn_gr; + + sysdb_grattrs = sysdb_new_attrs(test_ctx); + if (sysdb_grattrs == NULL) { + return ENOMEM; + } + + ret = sysdb_attrs_get_string(ldap_attrs, SYSDB_ORIG_DN, &s); + if (ret != EOK) { + talloc_free(sysdb_grattrs); + return ret; + } + + ret = sysdb_attrs_add_string(sysdb_grattrs, SYSDB_ORIG_DN, s); + if (ret != EOK) { + talloc_free(sysdb_grattrs); + return ret; + } + + fqdn_gr = sss_create_internal_fqname(test_ctx, gr->gr_name, + test_ctx->tctx->dom->name); + if (fqdn_gr == NULL) { + talloc_free(sysdb_grattrs); + return ENOMEM; + } + + ret = sysdb_store_group(test_ctx->tctx->dom, + fqdn_gr, gr->gr_gid, + sysdb_grattrs, 0, time(NULL)); + talloc_free(fqdn_gr); + talloc_free(sysdb_grattrs); + if (ret != EOK) { + return ret; + } + + return EOK; +} + +static errno_t +nested_group_test_link_group(struct nested_groups_test_ctx *test_ctx, + const char *shortname_parent, + const char *shortname_child) +{ + errno_t ret; + char *fqdn_parent; + char *fqdn_child; + + fqdn_parent = sss_create_internal_fqname(test_ctx, shortname_parent, + test_ctx->tctx->dom->name); + if (fqdn_parent == NULL) { + return ENOMEM; + } + + fqdn_child = sss_create_internal_fqname(test_ctx, shortname_child, + test_ctx->tctx->dom->name); + if (fqdn_child == NULL) { + return ENOMEM; + } + + ret = sysdb_add_group_member(test_ctx->tctx->dom, + fqdn_parent, + fqdn_child, + SYSDB_MEMBER_GROUP, false); + talloc_free(fqdn_parent); + talloc_free(fqdn_child); + return ret; +} + +static void assert_sysdb_name_equal(struct nested_groups_test_ctx *test_ctx, + struct ldb_message *msg, + const char *shortname) +{ + const char *s; + char *fqname; + + fqname = sss_create_internal_fqname(test_ctx, shortname, + test_ctx->tctx->dom->name); + assert_non_null(fqname); + + s = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + assert_string_equal(s, fqname); + talloc_free(fqname); +} + +static void assert_member_dn(struct nested_groups_test_ctx *test_ctx, + const char *member_name, + const char *exp_member_name) +{ + const char *s; + char *fqname; + + fqname = sss_create_internal_fqname(test_ctx, exp_member_name, + test_ctx->tctx->dom->name); + assert_non_null(fqname); + + s = sysdb_group_strdn(test_ctx, + test_ctx->tctx->dom->name, + fqname); + talloc_free(fqname); + assert_non_null(s); + + assert_string_equal(member_name, s); +} + +static void nested_group_external_member_test(void **state) +{ + struct nested_groups_test_ctx *test_ctx = talloc_get_type(*state, + struct nested_groups_test_ctx); + struct tevent_req *req; + errno_t ret; + struct sysdb_attrs *rootgroup_ldap_attrs = NULL; + struct sysdb_attrs *nested_group_ldap_attrs = NULL; + struct sysdb_attrs *ext_group_ldap_attrs = NULL; + struct sysdb_attrs *ext_group_nested_ldap_attrs = NULL; + struct ldb_result *res; + struct group rootgroup; + struct group nested_group; + struct group ext_group; + struct group ext_group_nested; + const char *rootgroup_members[] = { + "cn=nested_group,"GROUP_BASE_DN, + "cn=extgroup,"GROUP_BASE_DN, + NULL + }; + const char *nestedgroup_members[] = { + "cn=extgroup_nested,"GROUP_BASE_DN, + NULL + }; + const char *extgroup_members[] = { + "S-1-5-21-3623811015-3361044348-30300820-10001", + NULL + }; + const char *extgroup_nested_members[] = { + "S-1-5-21-3623811015-3361044348-30300820-10001", + "S-1-5-21-3623811015-3361044348-30300820-20001", + NULL + }; + const struct sysdb_attrs *nested_group_reply[2] = { NULL }; + struct ldb_message *msg; + struct ldb_message_element *member; + const char *sysdb_gr_attrs[] = { SYSDB_MEMBEROF, + NULL + }; + TALLOC_CTX *req_mem_ctx = NULL; + char *fqdn; + + /* LDAP provider doesn't support external groups by default */ + test_ctx->sdap_opts->group_map[SDAP_AT_GROUP_MEMBER].name = \ + discard_const(TEST_EXT_MEMBER); + test_ctx->sdap_opts->ext_ctx = test_ctx->ext_ctx; + + rootgroup.gr_name = discard_const("rootgroup"); + rootgroup.gr_gid = 1000; + rootgroup_ldap_attrs = mock_sysdb_group_rfc2307bis(test_ctx, + GROUP_BASE_DN, + rootgroup.gr_gid, + rootgroup.gr_name, + rootgroup_members); + assert_non_null(rootgroup_ldap_attrs); + + nested_group.gr_name = discard_const("nested_group"); + nested_group.gr_gid = 1001; + nested_group_ldap_attrs = mock_sysdb_group_rfc2307bis(test_ctx, + GROUP_BASE_DN, + nested_group.gr_gid, + nested_group.gr_name, + nestedgroup_members); + assert_non_null(nested_group_ldap_attrs); + nested_group_reply[0] = nested_group_ldap_attrs; + will_return(sdap_get_generic_recv, 1); + will_return(sdap_get_generic_recv, nested_group_reply); + will_return(sdap_get_generic_recv, ERR_OK); + + ext_group.gr_name = discard_const("extgroup"); + ext_group.gr_gid = 2001; + ext_group_ldap_attrs = mock_group_with_ext_members(test_ctx, + ext_group.gr_name, + ext_group.gr_gid, + extgroup_members); + assert_non_null(ext_group_ldap_attrs); + + ext_group_nested.gr_name = discard_const("extgroup_nested"); + ext_group_nested.gr_gid = 2002; + ext_group_nested_ldap_attrs = mock_group_with_ext_members(test_ctx, + ext_group_nested.gr_name, + ext_group_nested.gr_gid, + extgroup_nested_members); + assert_non_null(ext_group_nested_ldap_attrs); + + /* run test, check for memory leaks */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + sss_will_return_always(sdap_has_deref_support, false); + req = sdap_nested_group_send(test_ctx, test_ctx->tctx->ev, + test_ctx->sdap_domain, test_ctx->sdap_opts, + test_ctx->sdap_handle, rootgroup_ldap_attrs); + assert_non_null(req); + tevent_req_set_callback(req, nested_groups_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx) == true); + talloc_zfree(req_mem_ctx); + assert_int_equal(ret, ERR_OK); + + /* Save the groups to sysdb so that external membership code can link + * external members against this group + */ + ret = nested_group_test_save_group(test_ctx, + rootgroup_ldap_attrs, + &rootgroup); + assert_int_equal(ret, EOK); + + ret = nested_group_test_save_group(test_ctx, + ext_group_ldap_attrs, + &ext_group); + assert_int_equal(ret, EOK); + + ret = nested_group_test_save_group(test_ctx, + nested_group_ldap_attrs, + &nested_group); + assert_int_equal(ret, EOK); + + ret = nested_group_test_save_group(test_ctx, + ext_group_nested_ldap_attrs, + &ext_group_nested); + assert_int_equal(ret, EOK); + + ret = nested_group_test_link_group(test_ctx, + rootgroup.gr_name, + ext_group.gr_name); + assert_int_equal(ret, EOK); + + ret = nested_group_test_link_group(test_ctx, + rootgroup.gr_name, + nested_group.gr_name); + assert_int_equal(ret, EOK); + + ret = nested_group_test_link_group(test_ctx, + nested_group.gr_name, + ext_group_nested.gr_name); + assert_int_equal(ret, EOK); + + /* Resolve external members */ + req_mem_ctx = talloc_new(global_talloc_context); + assert_non_null(req_mem_ctx); + check_leaks_push(req_mem_ctx); + + req = sdap_nested_group_lookup_external_send(test_ctx, test_ctx->tctx->ev, + test_ctx->tctx->dom, + test_ctx->ext_ctx, + test_ctx->missing_external); + assert_non_null(req); + tevent_req_set_callback(req, nested_external_done, test_ctx); + + test_ctx->tctx->done = false; + ret = test_ev_loop(test_ctx->tctx); + assert_true(check_leaks_pop(req_mem_ctx) == true); + talloc_zfree(req_mem_ctx); + assert_int_equal(ret, ERR_OK); + + /* Make sure that extuser1001 is a member of rootgroup now */ + fqdn = sss_create_internal_fqname(test_ctx, "ext_user10001", + test_ctx->tctx->dom->name); + assert_non_null(fqdn); + + ret = sysdb_initgroups(test_ctx, test_ctx->tctx->dom, fqdn, &res); + talloc_zfree(fqdn); + assert_int_equal(ret, EOK); + assert_sysdb_name_equal(test_ctx, res->msgs[1], rootgroup.gr_name); + assert_sysdb_name_equal(test_ctx, res->msgs[2], nested_group.gr_name); + + fqdn = sss_create_internal_fqname(test_ctx, "ext_group20001", + test_ctx->tctx->dom->name); + assert_non_null(fqdn); + + ret = sysdb_search_group_by_name(test_ctx, + test_ctx->tctx->dom, + fqdn, + sysdb_gr_attrs, + &msg); + assert_int_equal(ret, EOK); + member = ldb_msg_find_element(msg, SYSDB_MEMBEROF); + assert_int_equal(member->num_values, 2); + + assert_member_dn(test_ctx, + (const char *) member->values[0].data, + rootgroup.gr_name); + assert_member_dn(test_ctx, + (const char *) member->values[1].data, + nested_group.gr_name); +} + +static void test_get_enterprise_principal_string_filter(void **state) +{ + int ret; + char *ep_filter; + struct dp_option *no_krb5_realm_opt = default_basic_opts; + + struct dp_option *krb5_realm_opt; + + ret = dp_copy_defaults(NULL, default_basic_opts, SDAP_OPTS_BASIC, + &krb5_realm_opt); + assert_int_equal(ret, EOK); + + ret = dp_opt_set_string(krb5_realm_opt, SDAP_KRB5_REALM, "TEST.DOM"); + assert_int_equal(ret, EOK); + + ep_filter = get_enterprise_principal_string_filter(NULL, NULL, NULL, NULL); + assert_null(ep_filter); + + ep_filter = get_enterprise_principal_string_filter(NULL, "aBC", "p@d.c", + no_krb5_realm_opt); + assert_null(ep_filter); + + ep_filter = get_enterprise_principal_string_filter(NULL, "aBC", "p", + krb5_realm_opt); + assert_null(ep_filter); + + ep_filter = get_enterprise_principal_string_filter(NULL, "aBC", "p@d.c", + krb5_realm_opt); + assert_non_null(ep_filter); + assert_string_equal(ep_filter, "(aBC=p\\\\@d.c@TEST.DOM)"); + talloc_free(ep_filter); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + new_test(one_group_no_members), + new_test(one_group_unique_members), + new_test(one_group_unique_members_one_ignored), + new_test(one_group_dup_users), + new_test(one_group_unique_group_members), + new_test(one_group_dup_group_members), + new_test(nested_chain), + new_test(nested_chain_with_error), + cmocka_unit_test_setup_teardown(nested_group_external_member_test, + nested_group_external_member_setup, + nested_group_external_member_teardown), + cmocka_unit_test(test_get_enterprise_principal_string_filter), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + return rv; +} diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c new file mode 100644 index 0000000..c17a497 --- /dev/null +++ b/src/tests/cmocka/test_nss_srv.c @@ -0,0 +1,6218 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: NSS responder tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <arpa/inet.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" +#include "responder/common/negcache.h" +#include "responder/nss/nss_private.h" +#include "responder/nss/nss_protocol.h" +#include "sss_client/idmap/sss_nss_idmap.h" +#include "util/util_sss_idmap.h" +#include "util/crypto/sss_crypto.h" +#include "util/sss_endian.h" +#include "db/sysdb.h" +#include "db/sysdb_iphosts.h" +#include "db/sysdb_ipnetworks.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_nss_conf.ldb" +#define TEST_DOM_NAME "nss_test" +#define TEST_SUBDOM_NAME "test.subdomain" +#define TEST_ID_PROVIDER "ldap" +#define TEST_DOM_SID "S-1-5-21-444379608-1639770488-2995963434" + +struct sss_nss_test_ctx { + struct sss_test_ctx *tctx; + struct sss_domain_info *subdom; + + struct resp_ctx *rctx; + struct cli_ctx *cctx; + struct sss_cmd_table *sss_nss_cmds; + struct sss_nss_ctx *nctx; + + int ncache_hits; +}; + +#define EXTRA_ATTRS "phone", "mobile" + +/* This list comes from nsssrv.c:sss_nss_get_config() and must be kept aligned */ +#define ORIG_ATTRS SYSDB_SID_STR, \ + ORIGINALAD_PREFIX SYSDB_NAME, \ + ORIGINALAD_PREFIX SYSDB_UIDNUM, \ + ORIGINALAD_PREFIX SYSDB_GIDNUM, \ + ORIGINALAD_PREFIX SYSDB_HOMEDIR, \ + ORIGINALAD_PREFIX SYSDB_GECOS, \ + ORIGINALAD_PREFIX SYSDB_SHELL, \ + SYSDB_UPN, \ + SYSDB_DEFAULT_OVERRIDE_NAME, \ + SYSDB_AD_ACCOUNT_EXPIRES, \ + SYSDB_AD_USER_ACCOUNT_CONTROL, \ + SYSDB_SSH_PUBKEY, \ + SYSDB_USER_CERT, \ + SYSDB_USER_EMAIL, \ + SYSDB_ORIG_DN, \ + SYSDB_ORIG_MEMBEROF + + +const char *global_extra_attrs[] = { EXTRA_ATTRS, NULL }; +const char *global_orig_attrs[] = { ORIG_ATTRS, NULL }; +const char *global_full_attrs[] = { ORIG_ATTRS, EXTRA_ATTRS, NULL }; + + +struct sss_nss_test_ctx *sss_nss_test_ctx; + +/* Mock NSS structure */ +struct sss_nss_ctx * +mock_nctx(TALLOC_CTX *mem_ctx) +{ + struct sss_nss_ctx *nctx; + enum idmap_error_code err; + + nctx = talloc_zero(mem_ctx, struct sss_nss_ctx); + if (!nctx) { + return NULL; + } + + nctx->pwfield = discard_const("*"); + + err = sss_idmap_init(sss_idmap_talloc, nctx, sss_idmap_talloc_free, + &nctx->idmap_ctx); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_FATAL_FAILURE, "sss_idmap_init failed.\n"); + talloc_free(nctx); + return NULL; + } + + return nctx; +} + +/* Mock reading requests from a client. Use values passed from mock + * instead + */ +void __real_sss_packet_get_body(struct sss_packet *packet, + uint8_t **body, size_t *blen); + +void __wrap_sss_packet_get_body(struct sss_packet *packet, + uint8_t **body, size_t *blen) +{ + enum sss_test_wrapper_call wtype = sss_mock_type(enum sss_test_wrapper_call); + size_t len; + + if (wtype == WRAP_CALL_REAL) { + return __real_sss_packet_get_body(packet, body, blen); + } + + *body = sss_mock_ptr_type(uint8_t *); + len = sss_mock_type(size_t); + if (len == 0) { + len = strlen((const char *) *body)+1; + } + *blen = len; + return; +} + +/* Mock returning result to client. Terminate the unit test instead. */ +typedef int (*cmd_cb_fn_t)(uint32_t, uint8_t *, size_t ); + +static void set_cmd_cb(cmd_cb_fn_t fn) +{ + will_return(__wrap_sss_cmd_done, fn); +} + +void __wrap_sss_cmd_done(struct cli_ctx *cctx, void *freectx) +{ + struct cli_protocol *pctx; + struct sss_packet *packet; + uint8_t *body; + size_t blen; + cmd_cb_fn_t check_cb; + + check_cb = sss_mock_ptr_type(cmd_cb_fn_t); + + if (check_cb == NULL) { + sss_nss_test_ctx->tctx->error = ENOENT; + } else { + pctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); + packet = pctx->creq->out; + + __real_sss_packet_get_body(packet, &body, &blen); + + sss_nss_test_ctx->tctx->error = check_cb(sss_packet_get_status(packet), + body, blen); + } + + sss_nss_test_ctx->tctx->done = true; + talloc_free(freectx); +} + +enum sss_cli_command __wrap_sss_packet_get_cmd(struct sss_packet *packet) +{ + return sss_mock_type(enum sss_cli_command); +} + +int __wrap_sss_cmd_send_empty(struct cli_ctx *cctx, TALLOC_CTX *freectx) +{ + sss_nss_test_ctx->tctx->done = true; + sss_nss_test_ctx->tctx->error = ENOENT; + return EOK; +} + +/* Intercept negative cache lookups */ +int __real_sss_ncache_check_user(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, const char *name); + +int __wrap_sss_ncache_check_user(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, const char *name) +{ + int ret; + + ret = __real_sss_ncache_check_user(ctx, dom, name); + if (ret == EEXIST) { + sss_nss_test_ctx->ncache_hits++; + } + return ret; +} + +int __real_sss_ncache_check_upn(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, const char *name); + +int __wrap_sss_ncache_check_upn(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, const char *name) +{ + int ret; + + ret = __real_sss_ncache_check_upn(ctx, dom, name); + if (ret == EEXIST) { + sss_nss_test_ctx->ncache_hits++; + } + return ret; +} + +int __real_sss_ncache_check_uid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, uid_t uid); + +int __wrap_sss_ncache_check_uid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, uid_t uid) +{ + int ret; + + ret = __real_sss_ncache_check_uid(ctx, dom, uid); + if (ret == EEXIST) { + sss_nss_test_ctx->ncache_hits++; + } + return ret; +} + +int __real_sss_ncache_check_sid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, const char *sid); + +int __wrap_sss_ncache_check_sid(struct sss_nc_ctx *ctx, + struct sss_domain_info *dom, const char *sid) +{ + int ret; + + ret = __real_sss_ncache_check_sid(ctx, dom, sid); + if (ret == EEXIST) { + sss_nss_test_ctx->ncache_hits++; + } + return ret; +} + +int __real_sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert); + +int __wrap_sss_ncache_check_cert(struct sss_nc_ctx *ctx, const char *cert) +{ + int ret; + + ret = __real_sss_ncache_check_cert(ctx, cert); + if (ret == EEXIST) { + sss_nss_test_ctx->ncache_hits++; + } + return ret; +} + +/* Mock input from the client library */ +static void mock_input_user_or_group(const char *input) +{ + const char *copy; + const char *shortname; + const char *domname; + char *separator; + + copy = talloc_strdup(sss_nss_test_ctx, input); + assert_non_null(copy); + + separator = strrchr(copy, '@'); + if (separator == NULL) { + shortname = input; + domname = NULL; + } else { + *separator = '\0'; + shortname = copy; + domname = separator + 1; + } + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, input); + will_return(__wrap_sss_packet_get_body, 0); + + mock_parse_inp(shortname, domname, EOK); +} + +static void mock_input_user_or_group_ex(bool do_parse_inp, const char *input, + uint32_t flags) +{ + const char *copy; + const char *shortname; + const char *domname; + char *separator; + uint8_t *data; + size_t len; + + len = strlen(input); + len++; + data = talloc_size(sss_nss_test_ctx, len + sizeof(uint32_t)); + assert_non_null(data); + memcpy(data, input, len); + SAFEALIGN_COPY_UINT32(data + len, &flags, NULL); + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, data); + will_return(__wrap_sss_packet_get_body, len + sizeof(uint32_t)); + + if (do_parse_inp) { + copy = talloc_strdup(sss_nss_test_ctx, input); + assert_non_null(copy); + + separator = strrchr(copy, '@'); + if (separator == NULL) { + shortname = input; + domname = NULL; + } else { + *separator = '\0'; + shortname = copy; + domname = separator + 1; + } + + mock_parse_inp(shortname, domname, EOK); + } +} + +static void mock_input_upn(const char *upn) +{ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, upn); + will_return(__wrap_sss_packet_get_body, 0); + + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); +} + +static void mock_input_sid(const char *sid) +{ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, sid); + will_return(__wrap_sss_packet_get_body, 0); +} + +static void mock_input_cert(const char *cert) +{ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, cert); + will_return(__wrap_sss_packet_get_body, 0); +} + +static void mock_input_id(TALLOC_CTX *mem_ctx, uint32_t id) +{ + uint8_t *body; + + body = talloc_zero_array(mem_ctx, uint8_t, 4); + if (body == NULL) return; + + SAFEALIGN_SETMEM_UINT32(body, id, NULL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, body); + will_return(__wrap_sss_packet_get_body, sizeof(uint32_t)); +} + +static void mock_input_id_ex(TALLOC_CTX *mem_ctx, uint32_t id, uint32_t flags) +{ + uint8_t *body; + + body = talloc_zero_array(mem_ctx, uint8_t, 8); + if (body == NULL) return; + + SAFEALIGN_SETMEM_UINT32(body, id, NULL); + SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), flags, NULL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, body); + will_return(__wrap_sss_packet_get_body, 2 * sizeof(uint32_t)); +} + +static void mock_fill_user(void) +{ + /* One packet for the entry and one for num entries */ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); +} + +static void mock_fill_bysid(void) +{ + /* One packet for the entry and one for num entries */ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); +} + +static int parse_user_packet(uint8_t *body, size_t blen, struct passwd *pwd) +{ + size_t rp = 2 * sizeof(uint32_t); + + SAFEALIGN_COPY_UINT32(&pwd->pw_uid, body+rp, &rp); + SAFEALIGN_COPY_UINT32(&pwd->pw_gid, body+rp, &rp); + + /* Sequence of null terminated strings (name, passwd, gecos, dir, shell) */ + pwd->pw_name = (char *) body+rp; + rp += strlen(pwd->pw_name) + 1; + if (rp >= blen) return EINVAL; + + pwd->pw_passwd = (char *) body+rp; + rp += strlen(pwd->pw_passwd) + 1; + if (rp >= blen) return EINVAL; + + pwd->pw_gecos = (char *) body+rp; + rp += strlen(pwd->pw_gecos) + 1; + if (rp >= blen) return EINVAL; + + pwd->pw_dir = (char *) body+rp; + rp += strlen(pwd->pw_dir) + 1; + if (rp >= blen) return EINVAL; + + pwd->pw_shell = (char *) body+rp; + rp += strlen(pwd->pw_shell) + 1; + if (rp != blen) return EINVAL; + + return EOK; +} + +static int parse_group_packet(uint8_t *body, size_t blen, struct group *gr, uint32_t *nmem) +{ + size_t rp = 2 * sizeof(uint32_t); /* Len and reserved */ + unsigned i; + + SAFEALIGN_COPY_UINT32(&gr->gr_gid, body+rp, &rp); + SAFEALIGN_COPY_UINT32(nmem, body+rp, &rp); + + gr->gr_name = (char *) body+rp; + rp += strlen(gr->gr_name) + 1; + if (rp >= blen) return EINVAL; + + gr->gr_passwd = (char *) body+rp; + rp += strlen(gr->gr_passwd) + 1; + + gr->gr_mem = NULL; + + if (*nmem > 0) { + gr->gr_mem = talloc_zero_array(sss_nss_test_ctx, char *, *nmem); + if (gr->gr_mem == NULL) return ENOMEM; + + for (i = 0; i < *nmem; i++) { + if (rp >= blen) return EINVAL; + + gr->gr_mem[i] = talloc_strdup(gr->gr_mem, (char *) body+rp); + rp += strlen(gr->gr_mem[i]) + 1; + } + } + + /* Make sure we exactly matched the end of the packet */ + if (rp != blen) return EINVAL; + return EOK; +} + +static void check_initgr_packet(uint8_t *body, size_t blen, + gid_t *gids, size_t num_gids) +{ + size_t rp; + unsigned i; + gid_t cur_gid; + uint32_t num_ret; + + rp = 0; + SAFEALIGN_COPY_UINT32(&num_ret, body, NULL); + assert_int_equal(num_ret, num_gids); + + rp = 2 * sizeof(uint32_t); /* Len and reserved */ + + for (i = 0; i < num_gids; i++) { + SAFEALIGN_COPY_UINT32(&cur_gid, body + rp, &rp); + assert_int_equal(cur_gid, gids[i]); + } +} + +static errno_t store_user(struct sss_nss_test_ctx *ctx, + struct sss_domain_info *dom, + struct passwd *user, + struct sysdb_attrs *attrs, + time_t cache_update) +{ + errno_t ret; + char *fqname; + + fqname = sss_create_internal_fqname(ctx, + user->pw_name, + dom->name); + if (fqname == NULL) { + return ENOMEM; + } + + /* Prime the cache with a valid user */ + ret = sysdb_store_user(dom, + fqname, + user->pw_passwd, + user->pw_uid, + user->pw_gid, + user->pw_gecos, + user->pw_dir, + user->pw_shell, + NULL, attrs, + NULL, 300, cache_update); + talloc_free(fqname); + return ret; +} + +static errno_t delete_user(struct sss_nss_test_ctx *ctx, + struct sss_domain_info *dom, + struct passwd *user) +{ + errno_t ret; + char *fqname; + + fqname = sss_create_internal_fqname(ctx, + user->pw_name, + dom->name); + if (fqname == NULL) { + return ENOMEM; + } + + ret = sysdb_delete_user(dom, fqname, user->pw_uid); + + talloc_free(fqname); + return ret; +} + +static errno_t set_user_attr(struct sss_nss_test_ctx *ctx, + struct sss_domain_info *dom, + struct passwd *user, + struct sysdb_attrs *attrs) +{ + errno_t ret; + char *fqname; + + fqname = sss_create_internal_fqname(ctx, + user->pw_name, + dom->name); + if (fqname == NULL) { + return ENOMEM; + } + + ret = sysdb_set_user_attr(sss_nss_test_ctx->tctx->dom, + fqname, + attrs, SYSDB_MOD_REP); + talloc_free(fqname); + return ret; +} + +static int get_user(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *shortname, + struct ldb_result **_res) +{ + errno_t ret; + char *fqname; + + fqname = sss_create_internal_fqname(mem_ctx, shortname, + domain->name); + if (fqname == NULL) { + return ENOMEM; + } + + ret = sysdb_getpwnam(mem_ctx, domain, fqname, _res); + talloc_free(fqname); + return ret; +} + +static void assert_users_equal(struct passwd *a, struct passwd *b) +{ + assert_int_equal(a->pw_uid, b->pw_uid); + assert_int_equal(a->pw_gid, b->pw_gid); + assert_string_equal(a->pw_name, b->pw_name); + assert_string_equal(a->pw_shell, b->pw_shell); + assert_string_equal(a->pw_passwd, b->pw_passwd); +} + +static errno_t store_group(struct sss_nss_test_ctx *ctx, + struct sss_domain_info *dom, + struct group *group, + struct sysdb_attrs *attrs, + time_t cache_update) +{ + errno_t ret; + char *fqname; + + fqname = sss_create_internal_fqname(ctx, + group->gr_name, + dom->name); + if (fqname == NULL) { + return ENOMEM; + } + + ret = sysdb_store_group(dom, + fqname, + group->gr_gid, + attrs, 300, 0); + talloc_free(fqname); + return ret; +} + +static errno_t delete_group(struct sss_nss_test_ctx *ctx, + struct sss_domain_info *dom, + struct group *group) +{ + errno_t ret; + char *fqname; + + fqname = sss_create_internal_fqname(ctx, + group->gr_name, + dom->name); + + if (fqname == NULL) { + return ENOMEM; + } + + ret = sysdb_delete_group(dom, fqname, group->gr_gid); + + talloc_free(fqname); + return ret; +} + +static int cmp_func(const void *a, const void *b) +{ + char *str1 = *(char **)discard_const(a); + char *str2 = *(char **)discard_const(b); + + return strcmp(str1, str2); +} + +static void order_string_array(char **_list, int size) +{ + if (size < 2 || _list == NULL || *_list == NULL) { + /* Nothing to do */ + return; + } + + qsort(_list, size, sizeof(char *), cmp_func); + return; +} + +static void assert_groups_equal(struct group *expected, + struct group *gr, const int nmem) +{ + int i; + + assert_int_equal(gr->gr_gid, expected->gr_gid); + assert_string_equal(gr->gr_name, expected->gr_name); + assert_string_equal(gr->gr_passwd, expected->gr_passwd); + + order_string_array(gr->gr_mem, nmem); + order_string_array(expected->gr_mem, nmem); + + for (i = 0; i < nmem; i++) { + assert_string_equal(gr->gr_mem[i], expected->gr_mem[i]); + } +} + +static errno_t store_group_member(struct sss_nss_test_ctx *ctx, + 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) +{ + errno_t ret; + char *group_fqname = NULL; + char *member_fqname = NULL; + + group_fqname = sss_create_internal_fqname(ctx, + shortname_group, + group_dom->name); + if (group_fqname == NULL) { + return ENOMEM; + } + + member_fqname = sss_create_internal_fqname(ctx, + shortname_member, + member_dom->name); + if (member_fqname == NULL) { + talloc_free(group_fqname); + return ENOMEM; + } + + ret = sysdb_add_group_member(group_dom, + group_fqname, + member_fqname, + SYSDB_MEMBER_USER, false); + talloc_free(group_fqname); + talloc_free(member_fqname); + return ret; +} + +static errno_t remove_group_member(struct sss_nss_test_ctx *ctx, + 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) +{ + errno_t ret; + char *group_fqname = NULL; + char *member_fqname = NULL; + + group_fqname = sss_create_internal_fqname(ctx, + shortname_group, + group_dom->name); + if (group_fqname == NULL) { + return ENOMEM; + } + + member_fqname = sss_create_internal_fqname(ctx, + shortname_member, + member_dom->name); + if (member_fqname == NULL) { + talloc_free(group_fqname); + return ENOMEM; + } + + ret = sysdb_remove_group_member(group_dom, + group_fqname, + member_fqname, + type, + false); + + talloc_free(group_fqname); + talloc_free(member_fqname); + return ret; +} + +/* ====================== The tests =============================== */ +struct passwd getpwnam_usr = { + .pw_name = discard_const("testuser"), + .pw_uid = 123, + .pw_gid = 456, + .pw_dir = discard_const("/home/testuser"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +/* Check getting cached and valid user from cache. Account callback will + * not be called and test_sss_nss_getpwnam_check will make sure the user is + * the same as the test entered before starting + */ +static int test_sss_nss_getpwnam_check(uint32_t status, uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_users_equal(&pwd, &getpwnam_usr); + return EOK; +} + +void test_sss_nss_getpwnam(void **state) +{ + errno_t ret; + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getpwnam_usr, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testuser"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwnam_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Test that searching for a nonexistent user yields ENOENT. + * Account callback will be called + */ +void test_sss_nss_getpwnam_neg(void **state) +{ + errno_t ret; + + mock_input_user_or_group("testuser_neg"); + mock_account_recv_simple(); + + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + /* 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 + */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_user_or_group("testuser_neg"); + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + /* Negative cache was hit this time */ + assert_int_equal(sss_nss_test_ctx->ncache_hits, 1); +} + +struct passwd getpwnam_search_usr = { + .pw_name = discard_const("testuser_search"), + .pw_uid = 567, + .pw_gid = 890, + .pw_dir = discard_const("/home/testuser_search"), + .pw_gecos = discard_const("test search user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getpwnam_search_acct_cb(void *pvt) +{ + struct sss_nss_test_ctx *ctx = talloc_get_type(pvt, struct sss_nss_test_ctx); + + return store_user(ctx, ctx->tctx->dom, &getpwnam_search_usr, NULL, 0); +} + +static int test_sss_nss_getpwnam_search_check(uint32_t status, + uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_users_equal(&pwd, &getpwnam_search_usr); + return EOK; +} + +void test_sss_nss_getpwnam_search(void **state) +{ + errno_t ret; + struct ldb_result *res; + + mock_input_user_or_group("testuser_search"); + mock_account_recv(0, 0, NULL, test_sss_nss_getpwnam_search_acct_cb, sss_nss_test_ctx); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + mock_fill_user(); + set_cmd_cb(test_sss_nss_getpwnam_search_check); + + ret = get_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + "testuser_search", &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* test_sss_nss_getpwnam_search_check will check the user attributes */ + ret = get_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + "testuser_search", &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); +} + +/* 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. + */ + +struct passwd getpwnam_update = { + .pw_name = discard_const("testuser_update"), + .pw_uid = 10, + .pw_gid = 11, + .pw_dir = discard_const("/home/testuser"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getpwnam_update_acct_cb(void *pvt) +{ + struct sss_nss_test_ctx *ctx = talloc_get_type(pvt, struct sss_nss_test_ctx); + + getpwnam_update.pw_shell = discard_const("/bin/ksh"); + return store_user(ctx, ctx->tctx->dom, &getpwnam_update, NULL, 0); +} + +static int test_sss_nss_getpwnam_update_check(uint32_t status, + uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_users_equal(&pwd, &getpwnam_update); + return EOK; +} + +void test_sss_nss_getpwnam_update(void **state) +{ + errno_t ret; + struct ldb_result *res; + const char *shell; + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getpwnam_update, NULL, 1); + assert_int_equal(ret, EOK); + + /* Mock client input */ + mock_input_user_or_group("testuser_update"); + /* Mock client command */ + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + /* Call this function when user is updated by the mock DP request */ + mock_account_recv(0, 0, NULL, test_sss_nss_getpwnam_update_acct_cb, sss_nss_test_ctx); + /* Call this function to check what the responder returned to the client */ + set_cmd_cb(test_sss_nss_getpwnam_update_check); + /* Mock output buffer */ + mock_fill_user(); + + /* Fire the command */ + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Check the user was updated in the cache */ + ret = get_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + "testuser_update" , &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + + shell = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SHELL, NULL); + assert_string_equal(shell, "/bin/ksh"); +} + +/* Check that a FQDN is returned if the domain is FQDN-only and a + * FQDN is requested + */ +struct passwd getpwnam_fqdn = { + .pw_name = discard_const("testuser_fqdn"), + .pw_uid = 124, + .pw_gid = 457, + .pw_dir = discard_const("/home/testuser"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getpwnam_check_fqdn(uint32_t status, + uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + sss_nss_test_ctx->cctx->rctx->domains[0].fqnames = false; + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + getpwnam_fqdn.pw_name = discard_const("testuser_fqdn@"TEST_DOM_NAME); + assert_users_equal(&pwd, &getpwnam_fqdn); + return EOK; +} + +void test_sss_nss_getpwnam_fqdn(void **state) +{ + errno_t ret; + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getpwnam_fqdn, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testuser_fqdn@"TEST_DOM_NAME); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwnam_check_fqdn); + sss_nss_test_ctx->cctx->rctx->domains[0].fqnames = true; + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Check that a user with a space in his username is returned fine. + */ +struct passwd getpwnam_space = { + .pw_name = discard_const("space user"), + .pw_uid = 225, + .pw_gid = 558, + .pw_dir = discard_const("/home/testuser"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getpwnam_check_space(uint32_t status, + uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_users_equal(&pwd, &getpwnam_space); + return EOK; +} + +void test_sss_nss_getpwnam_space(void **state) +{ + errno_t ret; + + /* Prime the cache with a valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getpwnam_space, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("space user"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwnam_check_space); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + +} + +static int test_sss_nss_getpwnam_check_space_sub(uint32_t status, + uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_int_equal(pwd.pw_uid, 225); + assert_int_equal(pwd.pw_gid, 558); + assert_string_equal(pwd.pw_name, "space_user"); + assert_string_equal(pwd.pw_shell, "/bin/sh"); + return EOK; +} + +void test_sss_nss_getpwnam_space_sub(void **state) +{ + errno_t ret; + + /* Set whitespace substitution */ + sss_nss_test_ctx->rctx->override_space = '_'; + + mock_input_user_or_group("space user"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwnam_check_space_sub); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + sss_nss_test_ctx->rctx->override_space = '\0'; + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getpwnam_space_sub_query(void **state) +{ + errno_t ret; + + /* Set whitespace substitution */ + sss_nss_test_ctx->rctx->override_space = '_'; + + mock_input_user_or_group("space_user"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwnam_check_space_sub); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + sss_nss_test_ctx->rctx->override_space = '\0'; + assert_int_equal(ret, EOK); +} + +/* + * Check that FQDN processing is able to handle arbitrarily sized + * delimiter + */ +struct passwd getpwnam_fancy_fqdn = { + .pw_name = discard_const("testuser_fqdn_fancy"), + .pw_uid = 125, + .pw_gid = 458, + .pw_dir = discard_const("/home/testuser"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getpwnam_check_fancy_fqdn(uint32_t status, + uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + sss_nss_test_ctx->cctx->rctx->domains[0].fqnames = false; + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_int_equal(pwd.pw_uid, 125); + assert_int_equal(pwd.pw_gid, 458); + assert_string_equal(pwd.pw_name, "testuser_fqdn_fancy@@@@@"TEST_DOM_NAME); + assert_string_equal(pwd.pw_shell, "/bin/sh"); + return EOK; +} + +void test_sss_nss_getpwnam_fqdn_fancy(void **state) +{ + errno_t ret; + + /* Prime the cache with a valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getpwnam_fancy_fqdn, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testuser_fqdn_fancy@"TEST_DOM_NAME); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwnam_check_fancy_fqdn); + sss_nss_test_ctx->cctx->rctx->domains[0].fqnames = true; + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Check getting cached and valid id from cache. Account callback will + * not be called and test_sss_nss_getpwuid_check will make sure the id is + * the same as the test entered before starting + */ +struct passwd getpwuid_usr = { + .pw_name = discard_const("testuser1"), + .pw_uid = 101, + .pw_gid = 401, + .pw_dir = discard_const("/home/testuser1"), + .pw_gecos = discard_const("test user1"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getpwuid_check(uint32_t status, uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_users_equal(&pwd, &getpwuid_usr); + return EOK; +} + +void test_sss_nss_getpwuid(void **state) +{ + errno_t ret; + uint32_t id = 101; + + /* Prime the cache with a valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getpwuid_usr, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_id(sss_nss_test_ctx, id); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID); + mock_fill_user(); + + /* Query for that id, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwuid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Test that searching for a nonexistent id yields ENOENT. + * Account callback will be called + */ +void test_sss_nss_getpwuid_neg(void **state) +{ + errno_t ret; + uid_t uid_neg = 102; + + mock_input_id(sss_nss_test_ctx, uid_neg); + mock_account_recv_simple(); + + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + /* 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 + */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_id(sss_nss_test_ctx, uid_neg); + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + /* Negative cache was hit this time */ + assert_int_equal(sss_nss_test_ctx->ncache_hits, 1); +} + +/* Test that lookup by UID for a user that does + * not exist in the cache fetches the user from DP + */ +struct passwd getpwuid_srch = { + .pw_name = discard_const("exampleuser_search"), + .pw_uid = 107, + .pw_gid = 987, + .pw_dir = discard_const("/home/examplesearch"), + .pw_gecos = discard_const("example search"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getpwuid_search_acct_cb(void *pvt) +{ + struct sss_nss_test_ctx *ctx = talloc_get_type(pvt, struct sss_nss_test_ctx); + + return store_user(ctx, ctx->tctx->dom, &getpwuid_srch, NULL, 0); +} + +static int test_sss_nss_getpwuid_search_check(uint32_t status, + uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_users_equal(&pwd, &getpwuid_srch); + return EOK; +} + +void test_sss_nss_getpwuid_search(void **state) +{ + errno_t ret; + struct ldb_result *res; + + mock_input_id(sss_nss_test_ctx, getpwuid_srch.pw_uid); + mock_account_recv(0, 0, NULL, test_sss_nss_getpwuid_search_acct_cb, sss_nss_test_ctx); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID); + mock_fill_user(); + set_cmd_cb(test_sss_nss_getpwuid_search_check); + + ret = sysdb_getpwuid(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + getpwuid_srch.pw_uid, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* test_sss_nss_getpwuid_search_check will check the id attributes */ + ret = sysdb_getpwuid(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + getpwuid_srch.pw_uid, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); +} + +/* 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. + */ +struct passwd getpwuid_update = { + .pw_name = discard_const("exampleuser_update"), + .pw_uid = 109, + .pw_gid = 11000, + .pw_dir = discard_const("/home/exampleuser"), + .pw_gecos = discard_const("example user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getpwuid_update_acct_cb(void *pvt) +{ + struct sss_nss_test_ctx *ctx = talloc_get_type(pvt, struct sss_nss_test_ctx); + + getpwuid_update.pw_shell = discard_const("/bin/ksh"); + return store_user(ctx, ctx->tctx->dom, &getpwuid_update, NULL, 0); +} + +static int test_sss_nss_getpwuid_update_check(uint32_t status, + uint8_t *body, size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_users_equal(&pwd, &getpwuid_update); + return EOK; +} + +void test_sss_nss_getpwuid_update(void **state) +{ + errno_t ret; + struct ldb_result *res; + const char *shell; + + /* Prime the cache with a valid but expired user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getpwuid_update, NULL, 1); + assert_int_equal(ret, EOK); + + /* Mock client input */ + mock_input_id(sss_nss_test_ctx, getpwuid_update.pw_uid); + /* Mock client command */ + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID); + /* Call this function when id is updated by the mock DP request */ + mock_account_recv(0, 0, NULL, test_sss_nss_getpwuid_update_acct_cb, sss_nss_test_ctx); + /* Call this function to check what the responder returned to the client */ + set_cmd_cb(test_sss_nss_getpwuid_update_check); + /* Mock output buffer */ + mock_fill_user(); + + /* Fire the command */ + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Check the user was updated in the cache */ + ret = sysdb_getpwuid(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + getpwuid_update.pw_uid, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + + shell = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SHELL, NULL); + assert_string_equal(shell, "/bin/ksh"); +} + +/* Testsuite setup and teardown */ +void test_sss_nss_setup(struct sss_test_conf_param params[], + void **state) +{ + errno_t ret; + + sss_nss_test_ctx = talloc_zero(NULL, struct sss_nss_test_ctx); + assert_non_null(sss_nss_test_ctx); + + sss_nss_test_ctx->tctx = create_dom_test_ctx(sss_nss_test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, params); + assert_non_null(sss_nss_test_ctx->tctx); + + sss_nss_test_ctx->tctx->dom->domain_id = discard_const(TEST_DOM_SID); + + sss_nss_test_ctx->sss_nss_cmds = get_sss_nss_cmds(); + assert_non_null(sss_nss_test_ctx->sss_nss_cmds); + + /* FIXME - perhaps this should be folded into sssd_domain_init or strictly + * used together + */ + ret = sss_names_init(sss_nss_test_ctx, sss_nss_test_ctx->tctx->confdb, + TEST_DOM_NAME, &sss_nss_test_ctx->tctx->dom->names); + assert_int_equal(ret, EOK); + + /* Initialize the NSS responder */ + sss_nss_test_ctx->nctx = mock_nctx(sss_nss_test_ctx); + assert_non_null(sss_nss_test_ctx->nctx); + + sss_nss_test_ctx->rctx = mock_rctx(sss_nss_test_ctx, sss_nss_test_ctx->tctx->ev, + sss_nss_test_ctx->tctx->dom, sss_nss_test_ctx->nctx); + assert_non_null(sss_nss_test_ctx->rctx); + sss_nss_test_ctx->rctx->cdb = sss_nss_test_ctx->tctx->confdb; + sss_nss_test_ctx->nctx->rctx = sss_nss_test_ctx->rctx; + + ret = sss_ad_default_names_ctx(sss_nss_test_ctx->nctx, + &sss_nss_test_ctx->nctx->rctx->global_names); + assert_int_equal(ret, EOK); + assert_non_null(sss_nss_test_ctx->nctx->rctx->global_names); + + /* Create client context */ + sss_nss_test_ctx->cctx = mock_cctx(sss_nss_test_ctx, sss_nss_test_ctx->rctx); + assert_non_null(sss_nss_test_ctx->cctx); + + /* Add nss specific state_ctx */ + sss_nss_connection_setup(sss_nss_test_ctx->cctx); + assert_non_null(sss_nss_test_ctx->cctx->state_ctx); + + /* do after previous setup as the former nulls protocol_ctx */ + sss_nss_test_ctx->cctx->protocol_ctx = mock_prctx(sss_nss_test_ctx->cctx); + assert_non_null(sss_nss_test_ctx->cctx->protocol_ctx); + + sss_nss_test_ctx->nctx->full_attribute_list = global_orig_attrs; +} + +struct group getgrnam_no_members = { + .gr_gid = 1123, + .gr_name = discard_const("testgroup"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +static int test_sss_nss_getgrnam_no_members_check(uint32_t status, + uint8_t *body, size_t blen) +{ + int ret; + uint32_t nmem; + struct group gr; + + assert_int_equal(status, EOK); + + ret = parse_group_packet(body, blen, &gr, &nmem); + assert_int_equal(ret, EOK); + assert_int_equal(nmem, 0); + + assert_groups_equal(&getgrnam_no_members, &gr, nmem); + return EOK; +} + +/* Test that requesting a valid, cached group with no members returns a valid + * group structure + */ +void test_sss_nss_getgrnam_no_members(void **state) +{ + errno_t ret; + + /* Prime the cache with a valid group */ + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getgrnam_no_members, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group(getgrnam_no_members.gr_name); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_no_members_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +struct passwd testmember1 = { + .pw_name = discard_const("testmember1"), + .pw_uid = 2001, + .pw_gid = 456, + .pw_dir = discard_const("/home/testmember1"), + .pw_gecos = discard_const("test member1"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct passwd testmember2 = { + .pw_name = discard_const("testmember2"), + .pw_uid = 2002, + .pw_gid = 456, + .pw_dir = discard_const("/home/testmember2"), + .pw_gecos = discard_const("test member2"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct group testgroup_members = { + .gr_gid = 1124, + .gr_name = discard_const("testgroup_members"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +static int test_sss_nss_getgrnam_members_check(uint32_t status, + uint8_t *body, size_t blen) +{ + int ret; + uint32_t nmem; + struct group gr; + const char *exp_members[] = { testmember1.pw_name, + testmember2.pw_name }; + struct group expected = { + .gr_gid = testgroup_members.gr_gid, + .gr_name = testgroup_members.gr_name, + .gr_passwd = testgroup_members.gr_passwd, + .gr_mem = discard_const(exp_members) + }; + + assert_int_equal(status, EOK); + + ret = parse_group_packet(body, blen, &gr, &nmem); + assert_int_equal(ret, EOK); + assert_int_equal(nmem, 2); + + assert_groups_equal(&expected, &gr, nmem); + return EOK; +} + +/* Test that requesting a valid, cached group with some members returns a valid + * group structure with those members present + */ +void test_sss_nss_getgrnam_members(void **state) +{ + errno_t ret; + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testgroup_members, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testmember1, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testmember2, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testgroup_members.gr_name, + sss_nss_test_ctx->tctx->dom, + testmember1.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testgroup_members.gr_name, + sss_nss_test_ctx->tctx->dom, + testmember2.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testgroup_members"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_members_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int test_sss_nss_getgrnam_members_check_fqdn(uint32_t status, + uint8_t *body, size_t blen) +{ + int ret; + uint32_t nmem; + struct group gr; + const char *exp_members[2]; + struct group expected = { + .gr_gid = testgroup_members.gr_gid, + .gr_passwd = testgroup_members.gr_passwd, + .gr_mem = discard_const(exp_members) + }; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(sss_nss_test_ctx); + assert_non_null(tmp_ctx); + + exp_members[0] = sss_tc_fqname(tmp_ctx, sss_nss_test_ctx->tctx->dom->names, + sss_nss_test_ctx->tctx->dom, testmember1.pw_name); + assert_non_null(exp_members[0]); + exp_members[1] = sss_tc_fqname(tmp_ctx, sss_nss_test_ctx->tctx->dom->names, + sss_nss_test_ctx->tctx->dom, testmember2.pw_name); + assert_non_null(exp_members[1]); + + expected.gr_name = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->tctx->dom->names, + sss_nss_test_ctx->tctx->dom, + testgroup_members.gr_name); + assert_non_null(expected.gr_name); + + assert_int_equal(status, EOK); + + ret = parse_group_packet(body, blen, &gr, &nmem); + assert_int_equal(ret, EOK); + assert_int_equal(nmem, 2); + + assert_groups_equal(&expected, &gr, nmem); + assert_int_equal(ret, EOK); + + talloc_free(tmp_ctx); + 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 + */ +void test_sss_nss_getgrnam_members_fqdn(void **state) +{ + errno_t ret; + + sss_nss_test_ctx->tctx->dom->fqnames = true; + + mock_input_user_or_group("testgroup_members@"TEST_DOM_NAME); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_members_check_fqdn); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + + /* Restore FQDN settings */ + sss_nss_test_ctx->tctx->dom->fqnames = false; + assert_int_equal(ret, EOK); +} + +/* Test that requesting a valid, cached group with subdomain members returns + * a valid * group structure with those members present as fully + * qualified names + */ +struct passwd submember1 = { + .pw_name = discard_const("submember1"), + .pw_uid = 4001, + .pw_gid = 456, + .pw_dir = discard_const("/home/submember1"), + .pw_gecos = discard_const("sub member1"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct passwd submember2 = { + .pw_name = discard_const("submember2"), + .pw_uid = 4002, + .pw_gid = 456, + .pw_dir = discard_const("/home/submember2"), + .pw_gecos = discard_const("sub member2"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct group testsubdomgroup = { + .gr_gid = 2002, + .gr_name = discard_const("testsubdomgroup"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +static int test_sss_nss_getgrnam_members_check_subdom(uint32_t status, + uint8_t *body, size_t blen) +{ + int ret; + uint32_t nmem; + struct group gr; + const char *exp_members[2]; + struct group expected = { + .gr_gid = testsubdomgroup.gr_gid, + .gr_passwd = testsubdomgroup.gr_passwd, + .gr_mem = discard_const(exp_members) + }; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(sss_nss_test_ctx); + assert_non_null(tmp_ctx); + + exp_members[0] = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->subdom->names, + sss_nss_test_ctx->subdom, + submember1.pw_name); + assert_non_null(exp_members[0]); + + exp_members[1] = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->subdom->names, + sss_nss_test_ctx->subdom, + submember2.pw_name); + assert_non_null(exp_members[1]); + + expected.gr_name = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->subdom->names, + sss_nss_test_ctx->subdom, + testsubdomgroup.gr_name); + assert_non_null(expected.gr_name); + + assert_int_equal(status, EOK); + + ret = parse_group_packet(body, blen, &gr, &nmem); + assert_int_equal(ret, EOK); + assert_int_equal(nmem, 2); + + assert_groups_equal(&expected, &gr, nmem); + assert_int_equal(ret, EOK); + + talloc_free(tmp_ctx); + return EOK; +} + +void test_sss_nss_getgrnam_members_subdom(void **state) +{ + errno_t ret; + + mock_input_user_or_group("testsubdomgroup@"TEST_SUBDOM_NAME); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_members_check_subdom); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getgrnam_members_subdom_nonfqnames(void **state) +{ + errno_t ret; + + mock_input_user_or_group("testsubdomgroup"); + mock_account_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_members_check_subdom); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + + assert_int_equal(ret, EOK); +} + +static int test_sss_nss_getgrnam_check_mix_dom(uint32_t status, + uint8_t *body, size_t blen) +{ + int ret; + uint32_t nmem; + struct group gr; + const char *exp_members[3]; + struct group expected = { + .gr_name = testgroup_members.gr_name, + .gr_gid = testgroup_members.gr_gid, + .gr_passwd = testgroup_members.gr_passwd, + .gr_mem = discard_const(exp_members) + }; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(sss_nss_test_ctx); + assert_non_null(tmp_ctx); + + exp_members[0] = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->subdom->names, + sss_nss_test_ctx->subdom, + submember1.pw_name); + assert_non_null(exp_members[0]); + exp_members[1] = testmember1.pw_name; + exp_members[2] = testmember2.pw_name; + + assert_int_equal(status, EOK); + + ret = parse_group_packet(body, blen, &gr, &nmem); + assert_int_equal(ret, EOK); + assert_int_equal(nmem, 3); + + assert_groups_equal(&expected, &gr, nmem); + assert_int_equal(ret, EOK); + + talloc_free(tmp_ctx); + return EOK; +} + +void test_sss_nss_getgrnam_mix_dom(void **state) +{ + errno_t ret; + + ret = store_group_member(sss_nss_test_ctx, + testgroup_members.gr_name, + sss_nss_test_ctx->tctx->dom, + submember1.pw_name, + sss_nss_test_ctx->subdom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testgroup_members"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_check_mix_dom); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getgrnam_mix_dom_nonfqnames(void **state) +{ + errno_t ret; + + ret = store_group_member(sss_nss_test_ctx, + testgroup_members.gr_name, + sss_nss_test_ctx->tctx->dom, + submember1.pw_name, + sss_nss_test_ctx->subdom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testgroup_members"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_check_mix_dom); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int test_sss_nss_getgrnam_check_mix_dom_fqdn(uint32_t status, + uint8_t *body, size_t blen) +{ + int ret; + uint32_t nmem; + struct group gr; + const char *exp_members[3]; + struct group expected = { + .gr_gid = testgroup_members.gr_gid, + .gr_passwd = testgroup_members.gr_passwd, + .gr_mem = discard_const(exp_members) + }; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(sss_nss_test_ctx); + assert_non_null(tmp_ctx); + + exp_members[0] = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->subdom->names, + sss_nss_test_ctx->subdom, + submember1.pw_name); + assert_non_null(exp_members[0]); + + if (sss_nss_test_ctx->tctx->dom->fqnames) { + exp_members[1] = sss_tc_fqname(tmp_ctx, sss_nss_test_ctx->tctx->dom->names, + sss_nss_test_ctx->tctx->dom, testmember1.pw_name); + assert_non_null(exp_members[1]); + exp_members[2] = sss_tc_fqname(tmp_ctx, sss_nss_test_ctx->tctx->dom->names, + sss_nss_test_ctx->tctx->dom, testmember2.pw_name); + assert_non_null(exp_members[2]); + + expected.gr_name = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->tctx->dom->names, + sss_nss_test_ctx->tctx->dom, + testgroup_members.gr_name); + assert_non_null(expected.gr_name); + } else { + exp_members[1] = testmember1.pw_name; + exp_members[2] = testmember2.pw_name; + expected.gr_name = testgroup_members.gr_name; + } + + assert_int_equal(status, EOK); + + ret = parse_group_packet(body, blen, &gr, &nmem); + assert_int_equal(ret, EOK); + assert_int_equal(nmem, 3); + + assert_groups_equal(&expected, &gr, nmem); + assert_int_equal(ret, EOK); + + talloc_free(tmp_ctx); + return EOK; +} + +void test_sss_nss_getgrnam_mix_dom_fqdn(void **state) +{ + errno_t ret; + + ret = store_group_member(sss_nss_test_ctx, + testgroup_members.gr_name, + sss_nss_test_ctx->tctx->dom, + submember1.pw_name, + sss_nss_test_ctx->subdom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + sss_nss_test_ctx->tctx->dom->fqnames = true; + + mock_input_user_or_group("testgroup_members@"TEST_DOM_NAME); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_check_mix_dom_fqdn); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + + /* Restore FQDN settings */ + sss_nss_test_ctx->tctx->dom->fqnames = false; + assert_int_equal(ret, EOK); +} + + +void test_sss_nss_getgrnam_mix_dom_fqdn_nonfqnames(void **state) +{ + errno_t ret; + + ret = store_group_member(sss_nss_test_ctx, + testgroup_members.gr_name, + sss_nss_test_ctx->tctx->dom, + submember1.pw_name, + sss_nss_test_ctx->subdom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testgroup_members"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_check_mix_dom_fqdn); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + + /* Restore FQDN settings */ + sss_nss_test_ctx->tctx->dom->fqnames = false; + assert_int_equal(ret, EOK); +} + +static int test_sss_nss_getgrnam_check_mix_subdom(uint32_t status, + uint8_t *body, size_t blen) +{ + int ret; + uint32_t nmem; + struct group gr; + const char *exp_members[3]; + struct group expected = { + .gr_gid = testsubdomgroup.gr_gid, + .gr_passwd = testsubdomgroup.gr_passwd, + .gr_mem = discard_const(exp_members) + }; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(sss_nss_test_ctx); + assert_non_null(tmp_ctx); + + exp_members[0] = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->subdom->names, + sss_nss_test_ctx->subdom, + submember1.pw_name); + assert_non_null(exp_members[0]); + + exp_members[1] = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->subdom->names, + sss_nss_test_ctx->subdom, + submember2.pw_name); + assert_non_null(exp_members[1]); + + /* Important: this member is from a non-qualified domain, so his name will + * not be qualified either + */ + exp_members[2] = testmember1.pw_name; + + expected.gr_name = sss_tc_fqname(tmp_ctx, + sss_nss_test_ctx->subdom->names, + sss_nss_test_ctx->subdom, + testsubdomgroup.gr_name); + assert_non_null(expected.gr_name); + + assert_int_equal(status, EOK); + + ret = parse_group_packet(body, blen, &gr, &nmem); + assert_int_equal(ret, EOK); + assert_int_equal(nmem, 3); + + assert_groups_equal(&expected, &gr, nmem); + assert_int_equal(ret, EOK); + + talloc_free(tmp_ctx); + return EOK; +} + +void test_sss_nss_getgrnam_mix_subdom(void **state) +{ + errno_t ret; + + ret = store_group_member(sss_nss_test_ctx, + testsubdomgroup.gr_name, + sss_nss_test_ctx->subdom, + testmember1.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testsubdomgroup@"TEST_SUBDOM_NAME); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_check_mix_subdom); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getgrnam_mix_subdom_nonfqnames(void **state) +{ + errno_t ret; + + ret = store_group_member(sss_nss_test_ctx, + testsubdomgroup.gr_name, + sss_nss_test_ctx->subdom, + testmember1.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testsubdomgroup"); + mock_account_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_check_mix_subdom); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +struct group space_group = { + .gr_gid = 2123, + .gr_name = discard_const("space group"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +static int test_sss_nss_getgrnam_space_check(uint32_t status, + uint8_t *body, size_t blen) +{ + int ret; + uint32_t nmem; + struct group gr; + + assert_int_equal(status, EOK); + + ret = parse_group_packet(body, blen, &gr, &nmem); + assert_int_equal(ret, EOK); + assert_int_equal(nmem, 0); + + assert_groups_equal(&space_group, &gr, nmem); + assert_int_equal(ret, EOK); + + return EOK; +} + +/* Test that requesting a valid, cached group with space in its name returns a valid + * group structure + */ +void test_sss_nss_getgrnam_space(void **state) +{ + errno_t ret; + + /* Prime the cache with a valid group */ + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &space_group, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("space group"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_space_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int test_sss_nss_getgrnam_space_sub_check(uint32_t status, + uint8_t *body, size_t blen) +{ + int ret; + uint32_t nmem; + struct group gr; + + assert_int_equal(status, EOK); + + ret = parse_group_packet(body, blen, &gr, &nmem); + assert_int_equal(ret, EOK); + assert_int_equal(nmem, 0); + + space_group.gr_name = discard_const("space_group"); + assert_groups_equal(&space_group, &gr, nmem); + assert_int_equal(ret, EOK); + + return EOK; +} + +/* Test that requesting a valid, cached group with space in its name returns a valid + * group structure + */ +void test_sss_nss_getgrnam_space_sub(void **state) +{ + errno_t ret; + + /* Set whitespace substitution */ + sss_nss_test_ctx->rctx->override_space = '_'; + + mock_input_user_or_group("space group"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_space_sub_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + sss_nss_test_ctx->rctx->override_space = '\0'; + assert_int_equal(ret, EOK); +} + +static int test_sss_nss_well_known_sid_check(uint32_t status, + uint8_t *body, size_t blen) +{ + const char *name; + enum sss_id_type type; + size_t rp = 2 * sizeof(uint32_t); + char *expected_result = sss_mock_ptr_type(char *); + + if (expected_result == NULL) { + assert_int_equal(status, EINVAL); + assert_int_equal(blen, 0); + } else { + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&type, body+rp, &rp); + + name = (char *) body+rp; + + assert_int_equal(type, SSS_ID_TYPE_GID); + assert_string_equal(name, expected_result); + } + + return EOK; +} + +void test_sss_nss_well_known_getnamebysid(void **state) +{ + errno_t ret; + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, "S-1-5-32-550"); + will_return(__wrap_sss_packet_get_body, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYSID); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(test_sss_nss_well_known_sid_check, "Print Operators@BUILTIN"); + + set_cmd_cb(test_sss_nss_well_known_sid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_well_known_getnamebysid_special(void **state) +{ + errno_t ret; + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, "S-1-2-0"); + will_return(__wrap_sss_packet_get_body, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYSID); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(test_sss_nss_well_known_sid_check, "LOCAL@LOCAL AUTHORITY"); + + set_cmd_cb(test_sss_nss_well_known_sid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_well_known_getnamebysid_non_existing(void **state) +{ + errno_t ret; + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, "S-1-5-32-123"); + will_return(__wrap_sss_packet_get_body, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYSID); + will_return(test_sss_nss_well_known_sid_check, NULL); + + set_cmd_cb(test_sss_nss_well_known_sid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_well_known_getidbysid_failure(void **state) +{ + errno_t ret; + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, "S-1-5-32-550"); + will_return(__wrap_sss_packet_get_body, 0); + will_return_always(__wrap_sss_packet_get_cmd, SSS_NSS_GETIDBYSID); + will_return(test_sss_nss_well_known_sid_check, NULL); + + set_cmd_cb(test_sss_nss_well_known_sid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETIDBYSID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_well_known_getsidbyname(void **state) +{ + errno_t ret; + const char *names[] = { "Cryptographic Operators@BUILTIN", + "BUILTIN\\Cryptographic Operators", NULL}; + size_t c; + + for (c = 0; names[c] != NULL; c++) { + sss_nss_test_ctx->tctx->done = false; + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, names[c]); + will_return(__wrap_sss_packet_get_body, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(test_sss_nss_well_known_sid_check, "S-1-5-32-569"); + + set_cmd_cb(test_sss_nss_well_known_sid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + } +} + +void test_sss_nss_well_known_getsidbyname_nonexisting(void **state) +{ + errno_t ret; + const char *names[] = { "Abc@BUILTIN", "BUILTIN\\Abc", NULL }; + size_t c; + + for (c = 0; names[c] != NULL; c++) { + sss_nss_test_ctx->tctx->done = false; + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, names[c]); + will_return(__wrap_sss_packet_get_body, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME); + will_return(test_sss_nss_well_known_sid_check, NULL); + + set_cmd_cb(test_sss_nss_well_known_sid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + } +} + +void test_sss_nss_well_known_getsidbyname_special(void **state) +{ + errno_t ret; + const char *names[] = { "CREATOR OWNER@CREATOR AUTHORITY", + "CREATOR AUTHORITY\\CREATOR OWNER", NULL }; + size_t c; + + for (c = 0; names[c] != NULL; c++) { + sss_nss_test_ctx->tctx->done = false; + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, names[c]); + will_return(__wrap_sss_packet_get_body, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(test_sss_nss_well_known_sid_check, "S-1-3-0"); + + set_cmd_cb(test_sss_nss_well_known_sid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + } +} + +static int test_sss_nss_getorigbyname_check(uint32_t status, uint8_t *body, + size_t blen) +{ + const char *s; + enum sss_id_type id_type; + size_t rp = 2 * sizeof(uint32_t); + + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&id_type, body+rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_UID); + + /* Sequence of null terminated strings */ + s = (char *) body+rp; + assert_string_equal(s, SYSDB_SID_STR); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "S-1-2-3-4"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, ORIGINALAD_PREFIX SYSDB_NAME); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "orig_name"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, ORIGINALAD_PREFIX SYSDB_UIDNUM); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "1234"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, SYSDB_UPN); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "testuserorig@upndomain.test"); + rp += strlen(s) + 1; + assert_int_equal(rp, blen); + + return EOK; +} + +struct passwd orig_name = { + .pw_name = discard_const("testuserorig"), + .pw_uid = 1234, + .pw_gid = 5678, + .pw_dir = discard_const("/home/testuserorig"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +void test_sss_nss_getorigbyname(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *test_upn = "testuserorig@upndomain.test"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, "S-1-2-3-4"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, ORIGINALAD_PREFIX SYSDB_NAME, + "orig_name"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_uint32(attrs, ORIGINALAD_PREFIX SYSDB_UIDNUM, 1234); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, test_upn); + assert_int_equal(ret, EOK); + + /* Prime the cache with a valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &orig_name, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testuserorig"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETORIGBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getorigbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETORIGBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Also test looking up the same stuff with UPN */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_upn(test_upn); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETORIGBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getorigbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETORIGBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int test_sss_nss_getorigbyname_extra_check(uint32_t status, uint8_t *body, + size_t blen) +{ + const char *s; + enum sss_id_type id_type; + size_t rp = 2 * sizeof(uint32_t); + + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&id_type, body+rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_UID); + + /* Sequence of null terminated strings */ + s = (char *) body+rp; + assert_string_equal(s, SYSDB_SID_STR); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "S-1-2-3-4"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, ORIGINALAD_PREFIX SYSDB_NAME); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "orig_name"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, ORIGINALAD_PREFIX SYSDB_UIDNUM); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "1234"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "phone"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "+12-34 56 78"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "mobile"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "+98-76 54 32"); + rp += strlen(s) + 1; + assert_int_equal(rp, blen); + + return EOK; +} + +struct passwd orig_extra = { + .pw_name = discard_const("testuserorigextra"), + .pw_uid = 2345, + .pw_gid = 6789, + .pw_dir = discard_const("/home/testuserorigextra"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +void test_sss_nss_getorigbyname_extra_attrs(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, "S-1-2-3-4"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, ORIGINALAD_PREFIX SYSDB_NAME, + "orig_name"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_uint32(attrs, ORIGINALAD_PREFIX SYSDB_UIDNUM, 1234); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, "phone", "+12-34 56 78"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, "mobile", "+98-76 54 32"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, "not_extra", "abc"); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &orig_extra, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testuserorigextra"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETORIGBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getorigbyname_extra_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETORIGBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int test_sss_nss_getorigbyname_multi_check(uint32_t status, uint8_t *body, + size_t blen) +{ + const char *s; + enum sss_id_type id_type; + size_t rp = 2 * sizeof(uint32_t); + + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&id_type, body+rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_UID); + + /* Sequence of null terminated strings */ + s = (char *) body+rp; + assert_string_equal(s, SYSDB_SID_STR); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "S-1-2-3-4"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, ORIGINALAD_PREFIX SYSDB_NAME); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "orig_name"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, ORIGINALAD_PREFIX SYSDB_UIDNUM); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "1234"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, SYSDB_ORIG_MEMBEROF); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "cn=abc"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, SYSDB_ORIG_MEMBEROF); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "cn=def"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, SYSDB_ORIG_MEMBEROF); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "cn=123"); + rp += strlen(s) + 1; + assert_int_equal(rp, blen); + + return EOK; +} + +struct passwd orig_multi = { + .pw_name = discard_const("testuserorigmulti"), + .pw_uid = 3456, + .pw_gid = 7890, + .pw_dir = discard_const("/home/testuserorigmulti"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +void test_sss_nss_getorigbyname_multi_value_attrs(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, "S-1-2-3-4"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, ORIGINALAD_PREFIX SYSDB_NAME, + "orig_name"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_uint32(attrs, ORIGINALAD_PREFIX SYSDB_UIDNUM, 1234); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_MEMBEROF, "cn=abc"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_MEMBEROF, "cn=def"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_MEMBEROF, "cn=123"); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &orig_multi, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testuserorigmulti"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETORIGBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getorigbyname_multi_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETORIGBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +struct passwd orig_user_dup = { + .pw_name = discard_const("testdup"), + .pw_uid = 3478, + .pw_gid = 3478, + .pw_dir = discard_const("/home/testdup"), + .pw_gecos = discard_const("test dup"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct group orig_group_dup = { + .gr_gid = 3478, + .gr_name = discard_const("testdup"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +static int test_sss_nss_getorigbyusername_check(uint32_t status, uint8_t *body, + size_t blen) +{ + const char *s; + enum sss_id_type id_type; + size_t rp = 2 * sizeof(uint32_t); + + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&id_type, body+rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_UID); + + /* Sequence of null terminated strings */ + s = (char *) body+rp; + assert_string_equal(s, SYSDB_SID_STR); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "S-1-2-3-4"); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, ORIGINALAD_PREFIX SYSDB_NAME); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "orig_name"); + rp += strlen(s) + 1; + assert_int_equal(rp, blen); + + return EOK; +} + +static int test_sss_nss_getorigbygroupname_check(uint32_t status, uint8_t *body, + size_t blen) +{ + const char *s; + enum sss_id_type id_type; + size_t rp = 2 * sizeof(uint32_t); + + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&id_type, body+rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_GID); + + /* Sequence of null terminated strings */ + s = (char *) body+rp; + assert_string_equal(s, SYSDB_SID_STR); + rp += strlen(s) + 1; + assert_true(rp < blen); + + s = (char *) body+rp; + assert_string_equal(s, "S-1-2-3-5"); + rp += strlen(s) + 1; + assert_int_equal(rp, blen); + + return EOK; +} + +static void test_sss_nss_getorigbyname_dup_add(void) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, "S-1-2-3-4"); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, ORIGINALAD_PREFIX SYSDB_NAME, + "orig_name"); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &orig_user_dup, attrs, 0); + assert_int_equal(ret, EOK); + talloc_free(attrs); + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, "S-1-2-3-5"); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &orig_group_dup, attrs, 0); + assert_int_equal(ret, EOK); + talloc_free(attrs); +} + + +static int test_sss_nss_EINVAL_check(uint32_t status, uint8_t *body, size_t blen); +/* test_sss_nss_getorigbyname_dup is expected to fail because there are a user and + * a group with the same name in the cache. */ +void test_sss_nss_getorigbyname_dup(void **state) +{ + errno_t ret; + + test_sss_nss_getorigbyname_dup_add(); + + mock_input_user_or_group("testdup"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETORIGBYNAME); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETORIGBYNAME); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETORIGBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getorigbyusername(void **state) +{ + errno_t ret; + + test_sss_nss_getorigbyname_dup_add(); + + mock_input_user_or_group("testdup"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETORIGBYUSERNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getorigbyusername_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETORIGBYUSERNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getorigbygroupname(void **state) +{ + errno_t ret; + + test_sss_nss_getorigbyname_dup_add(); + + mock_input_user_or_group("testdup"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETORIGBYGROUPNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getorigbygroupname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETORIGBYGROUPNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +struct passwd upn_user = { + .pw_name = discard_const("upnuser"), + .pw_uid = 34567, + .pw_gid = 45678, + .pw_dir = discard_const("/home/testuserorig"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getpwnam_upn_check(uint32_t status, + uint8_t *body, + size_t blen) +{ + struct passwd pwd; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_user_packet(body, blen, &pwd); + assert_int_equal(ret, EOK); + + assert_users_equal(&pwd, &upn_user); + return EOK; +} + +void test_sss_nss_getpwnam_upn(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, "upnuser@upndomain.test"); + assert_int_equal(ret, EOK); + + /* Prime the cache with a valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &upn_user, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_upn("upnuser@upndomain.test"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwnam_upn_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getpwnam_upn_same_domain(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, "upnuser_upn@" TEST_DOM_NAME); + assert_int_equal(ret, EOK); + + /* Prime the cache with a valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &upn_user, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("upnuser_upn@" TEST_DOM_NAME); + mock_account_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwnam_upn_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Test that searching for a nonexistent user yields ENOENT. + * Account callback will be called + */ +void test_sss_nss_getpwnam_upn_neg(void **state) +{ + errno_t ret; + + mock_input_upn("nosuchupnuser@upndomain.test"); + mock_account_recv_simple(); + + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + /* 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 + */ + sss_nss_test_ctx->tctx->done = false; + sss_nss_test_ctx->ncache_hits = 0; + + mock_input_upn("nosuchupnuser@upndomain.test"); + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + /* Negative cache was hit this time */ + assert_int_equal(sss_nss_test_ctx->ncache_hits, 1); +} + +static int test_sss_nss_initgr_check(uint32_t status, uint8_t *body, size_t blen) +{ + gid_t expected_gids[] = { 3211, 3212 }; + + assert_int_equal(status, EOK); + check_initgr_packet(body, blen, expected_gids, N_ELEMENTS(expected_gids)); + return EOK; +} + +struct passwd testinitgr_usr = { + .pw_name = discard_const("testinitgr"), + .pw_uid = 321, + .pw_gid = 654, + .pw_dir = discard_const("/home/testinitgr"), + .pw_gecos = discard_const("test initgroups"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct group testinitgr_gr1 = { + .gr_gid = 3211, + .gr_name = discard_const("testinitgr_gr1"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +struct group testinitgr_gr2 = { + .gr_gid = 3212, + .gr_name = discard_const("testinitgr_gr2"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +void test_sss_nss_initgroups(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, + time(NULL) + 300); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, "upninitgr@upndomain.test"); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_usr, attrs, 0); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_gr1, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_gr2, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testinitgr_gr1.gr_name, + sss_nss_test_ctx->tctx->dom, + testinitgr_usr.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testinitgr_gr2.gr_name, + sss_nss_test_ctx->tctx->dom, + testinitgr_usr.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testinitgr"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_initgr_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Test that searching for a nonexistent user yields ENOENT. + * Account callback will be called + */ +void test_initgr_neg_by_name(const char *name, bool is_upn) +{ + errno_t ret; + + if (is_upn) { + mock_input_upn(name); + } else { + mock_input_user_or_group(name); + } + mock_account_recv_simple(); + + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + /* 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 + */ + sss_nss_test_ctx->tctx->done = false; + sss_nss_test_ctx->ncache_hits = 0; + + if (is_upn) { + mock_input_upn(name); + } else { + mock_input_user_or_group(name); + } + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + /* Negative cache was hit this time */ + assert_int_equal(sss_nss_test_ctx->ncache_hits, 1); +} + +void test_sss_nss_initgr_neg(void **state) +{ + test_initgr_neg_by_name("testinitgr_neg", false); +} + +struct passwd testinitgr_srch_usr = { + .pw_name = discard_const("testinitgr_srch"), + .pw_uid = 421, + .pw_gid = 654, + .pw_dir = discard_const("/home/testinitgr_srch"), + .pw_gecos = discard_const("test initgroups"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct group testinitgr_srch_gr1 = { + .gr_gid = 4211, + .gr_name = discard_const("testinitgr_srch_gr1"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +struct group testinitgr_srch_gr2 = { + .gr_gid = 4212, + .gr_name = discard_const("testinitgr_srch_gr2"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +static int test_sss_nss_initgr_search_acct_cb(void *pvt) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, + time(NULL) + 300); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_srch_usr, attrs, 0); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_srch_gr1, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_srch_gr2, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testinitgr_srch_gr1.gr_name, + sss_nss_test_ctx->tctx->dom, + testinitgr_srch_usr.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testinitgr_srch_gr2.gr_name, + sss_nss_test_ctx->tctx->dom, + testinitgr_srch_usr.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + return EOK; +} + +static int test_sss_nss_initgr_search_check(uint32_t status, + uint8_t *body, size_t blen) +{ + gid_t expected_gids[] = { 4211, 4212 }; + + assert_int_equal(status, EOK); + check_initgr_packet(body, blen, expected_gids, N_ELEMENTS(expected_gids)); + return EOK; +} + +void test_sss_nss_initgr_search(void **state) +{ + errno_t ret; + struct ldb_result *res; + + mock_input_user_or_group("testinitgr_srch"); + mock_account_recv(0, 0, NULL, test_sss_nss_initgr_search_acct_cb, sss_nss_test_ctx); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + set_cmd_cb(test_sss_nss_initgr_search_check); + + ret = get_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + "testinitgr_srch", &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* test_sss_nss_getpwnam_search_check will check the user attributes */ + ret = get_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + "testinitgr_srch", &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); +} + +struct passwd testinitgr_update_usr = { + .pw_name = discard_const("testinitgr_update"), + .pw_uid = 521, + .pw_gid = 654, + .pw_dir = discard_const("/home/testinitgr_update"), + .pw_gecos = discard_const("test initgroups"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct group testinitgr_update_gr1 = { + .gr_gid = 5211, + .gr_name = discard_const("testinitgr_update_gr1"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +struct group testinitgr_update_gr2 = { + .gr_gid = 5212, + .gr_name = discard_const("testinitgr_update_gr2"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +static int test_sss_nss_initgr_update_acct_cb(void *pvt) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, + time(NULL) + 300); + assert_int_equal(ret, EOK); + + ret = set_user_attr(sss_nss_test_ctx, + sss_nss_test_ctx->tctx->dom, + &testinitgr_update_usr, + attrs); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_update_gr2, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testinitgr_update_gr2.gr_name, + sss_nss_test_ctx->tctx->dom, + testinitgr_update_usr.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + return EOK; +} + +static int test_sss_nss_initgr_update_check(uint32_t status, uint8_t *body, size_t blen) +{ + gid_t expected_gids[] = { 5211, 5212 }; + + assert_int_equal(status, EOK); + check_initgr_packet(body, blen, expected_gids, N_ELEMENTS(expected_gids)); + return EOK; +} + +void test_sss_nss_initgr_update(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, + time(NULL) - 1); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_update_usr, attrs, 0); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_update_gr1, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testinitgr_update_gr1.gr_name, + sss_nss_test_ctx->tctx->dom, + testinitgr_update_usr.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testinitgr_update"); + mock_account_recv(0, 0, NULL, test_sss_nss_initgr_update_acct_cb, sss_nss_test_ctx); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + set_cmd_cb(test_sss_nss_initgr_update_check); + + /* Query for that user, call a callback when command finishes */ + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +struct passwd testinitgr_2attr_usr = { + .pw_name = discard_const("testinitgr_2attr"), + .pw_uid = 521, + .pw_gid = 654, + .pw_dir = discard_const("/home/testinitgr_2attr"), + .pw_gecos = discard_const("test initgroups"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct group testinitgr_2attr_gr1 = { + .gr_gid = 5221, + .gr_name = discard_const("testinitgr_2attr_gr11"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +struct group testinitgr_2attr_gr2 = { + .gr_gid = 5222, + .gr_name = discard_const("testinitgr_2attr_gr12"), + .gr_passwd = discard_const("*"), + .gr_mem = NULL, +}; + +static int test_sss_nss_initgr_update_acct_2expire_attributes_cb(void *pvt) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, + time(NULL) + 300); + assert_int_equal(ret, EOK); + + ret = set_user_attr(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_2attr_usr, attrs); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_2attr_gr2, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testinitgr_2attr_gr2.gr_name, + sss_nss_test_ctx->tctx->dom, + testinitgr_2attr_usr.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + return EOK; +} + +static int test_sss_nss_initgr_update_2expire_attributes_check(uint32_t status, + uint8_t *body, + size_t blen) +{ + gid_t expected_gids[] = { 5221, 5222 }; + + assert_int_equal(status, EOK); + check_initgr_packet(body, blen, expected_gids, N_ELEMENTS(expected_gids)); + 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. + */ +void test_sss_nss_initgr_update_two_expire_attributes(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, + 0); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE, + time(NULL) + 100); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_2attr_usr, attrs, 0); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_2attr_gr1, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testinitgr_2attr_gr1.gr_name, + sss_nss_test_ctx->tctx->dom, + testinitgr_2attr_usr.pw_name, + sss_nss_test_ctx->tctx->dom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testinitgr_2attr"); + mock_account_recv(0, 0, NULL, + test_sss_nss_initgr_update_acct_2expire_attributes_cb, + sss_nss_test_ctx); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + set_cmd_cb(test_sss_nss_initgr_update_2expire_attributes_check); + + /* Query for that user, call a callback when command finishes */ + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_initgroups_upn(void **state) +{ + errno_t ret; + + mock_input_upn("upninitgr@upndomain.test"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR); + will_return_always(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_initgr_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Test that searching for a nonexistent user yields ENOENT. + * Account callback will be called + */ +void test_sss_nss_initgr_neg_upn(void **state) +{ + test_initgr_neg_by_name("upninitgr_neg@upndomain.test", true); +} + +static int sss_nss_test_setup(void **state) +{ + struct sss_test_conf_param params[] = { + { "enumerate", "false" }, + { NULL, NULL }, /* Sentinel */ + }; + + test_sss_nss_setup(params, state); + return 0; +} + +static int sss_nss_fqdn_test_setup(void **state) +{ + struct sss_test_conf_param params[] = { + { "enumerate", "false" }, + { "full_name_format", "%1$s@%2$s" }, + { NULL, NULL }, /* Sentinel */ + }; + + test_sss_nss_setup(params, state); + return 0; +} + +static int sss_nss_test_setup_extra_attr(void **state) +{ + struct sss_test_conf_param params[] = { + { "enumerate", "false" }, + { NULL, NULL }, /* Sentinel */ + }; + + test_sss_nss_setup(params, state); + + sss_nss_test_ctx->nctx->extra_attributes = global_extra_attrs; + sss_nss_test_ctx->nctx->full_attribute_list = global_full_attrs; + + return 0; +} + +static int sss_nss_subdom_test_setup_common(void **state, bool nonfqnames) +{ + const char *const testdom[4] = { TEST_SUBDOM_NAME, "TEST.SUB", "test", "S-3" }; + struct sss_domain_info *dom; + + struct sss_domain_info *subdomain; + errno_t ret; + + sss_nss_test_setup(state); + + subdomain = new_subdomain(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + testdom[0], testdom[1], testdom[2], testdom[0], + testdom[3], false, false, NULL, NULL, 0, + sss_nss_test_ctx->tctx->confdb, true); + assert_non_null(subdomain); + + ret = sysdb_subdomain_store(sss_nss_test_ctx->tctx->sysdb, + testdom[0], testdom[1], testdom[2], testdom[0], + testdom[3], MPG_DISABLED, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(sss_nss_test_ctx->tctx->dom, + sss_nss_test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + if (nonfqnames) { + for (dom = sss_nss_test_ctx->rctx->domains; + dom != NULL; + dom = get_next_domain(dom, SSS_GND_ALL_DOMAINS)) { + if (strcmp(dom->name, subdomain->name) == 0) { + dom->fqnames = false; + break; + } + } + } + + ret = sss_resp_populate_cr_domains(sss_nss_test_ctx->rctx); + assert_int_equal(ret, EOK); + assert_non_null(sss_nss_test_ctx->rctx->cr_domains); + + sss_nss_test_ctx->subdom = sss_nss_test_ctx->tctx->dom->subdomains; + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->subdom, + &testsubdomgroup, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->subdom, + &submember1, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->subdom, + &submember2, NULL, 0); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testsubdomgroup.gr_name, + sss_nss_test_ctx->subdom, + submember1.pw_name, + sss_nss_test_ctx->subdom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + ret = store_group_member(sss_nss_test_ctx, + testsubdomgroup.gr_name, + sss_nss_test_ctx->subdom, + submember2.pw_name, + sss_nss_test_ctx->subdom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + return 0; + +} + +static int sss_nss_subdom_test_setup(void **state) +{ + return sss_nss_subdom_test_setup_common(state, false); +} + +static int sss_nss_subdom_test_setup_nonfqnames(void **state) +{ + return sss_nss_subdom_test_setup_common(state, true); +} + +static int sss_nss_fqdn_fancy_test_setup(void **state) +{ + struct sss_test_conf_param params[] = { + { "enumerate", "false" }, + { "full_name_format", "%1$s@@@@@%2$s" }, + { NULL, NULL }, /* Sentinel */ + }; + + test_sss_nss_setup(params, state); + return 0; +} + +static int sss_nss_test_teardown(void **state) +{ + talloc_free(sss_nss_test_ctx); + return 0; +} + +static int sss_nss_subdom_test_teardown(void **state) +{ + errno_t ret; + + ret = remove_group_member(sss_nss_test_ctx, + testsubdomgroup.gr_name, + sss_nss_test_ctx->subdom, + submember2.pw_name, + sss_nss_test_ctx->subdom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + ret = remove_group_member(sss_nss_test_ctx, + testsubdomgroup.gr_name, + sss_nss_test_ctx->subdom, + submember1.pw_name, + sss_nss_test_ctx->subdom, + SYSDB_MEMBER_USER); + assert_int_equal(ret, EOK); + + ret = delete_user(sss_nss_test_ctx, sss_nss_test_ctx->subdom, &submember2); + assert_int_equal(ret, EOK); + + ret = delete_user(sss_nss_test_ctx, sss_nss_test_ctx->subdom, &submember1); + assert_int_equal(ret, EOK); + + ret = delete_group(sss_nss_test_ctx, sss_nss_test_ctx->subdom, &testsubdomgroup); + assert_int_equal(ret, EOK); + + return sss_nss_test_teardown(state); +} + +struct passwd testbysid = { + .pw_name = discard_const("testsiduser"), + .pw_uid = 12345, + .pw_gid = 6890, + .pw_dir = discard_const("/home/testsiduser"), + .pw_gecos = discard_const("test bysid lookup"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getnamebysid_check(uint32_t status, uint8_t *body, size_t blen) +{ + size_t rp = 2 * sizeof(uint32_t); /* num_results and reserved */ + uint32_t id_type; + const char *name; + + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&id_type, body+rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_UID); + + name = (const char *) body + rp; + assert_string_equal(name, testbysid.pw_name); + + return EOK; +} + +static void test_sss_nss_getnamebysid(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + char *user_sid; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + user_sid = talloc_asprintf(attrs, "%s-500", + sss_nss_test_ctx->tctx->dom->domain_id); + assert_non_null(user_sid); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, user_sid); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testbysid, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_sid(user_sid); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYSID); + mock_fill_bysid(); + + /* Query for that user, call a callback when command finishes */ + /* Should go straight to back end, without contacting DP */ + set_cmd_cb(test_sss_nss_getnamebysid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Test that searching for a nonexistent user yields ENOENT. + * Account callback will be called + */ +void test_sss_nss_getnamebysid_neg(void **state) +{ + errno_t ret; + char *user_sid; + + user_sid = talloc_asprintf(sss_nss_test_ctx, "%s-499", + sss_nss_test_ctx->tctx->dom->domain_id); + assert_non_null(user_sid); + + mock_input_sid(user_sid); + mock_account_recv_simple(); + + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + /* 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 + */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_sid(user_sid); + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + /* Negative cache was hit this time */ + assert_int_equal(sss_nss_test_ctx->ncache_hits, 1); +} + +struct passwd testbysid_update = { + .pw_name = discard_const("testsidbyname_update"), + .pw_uid = 123456, + .pw_gid = 789, + .pw_dir = discard_const("/home/testsidbyname_update"), + .pw_gecos = discard_const("test bysid lookup"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +static int test_sss_nss_getnamebysid_update_check(uint32_t status, + uint8_t *body, + size_t blen) +{ + size_t rp = 2 * sizeof(uint32_t); /* num_results and reserved */ + uint32_t id_type; + const char *name; + + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&id_type, body+rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_UID); + + name = (const char *) body + rp; + assert_string_equal(name, "testsidbyname_update"); + + return EOK; +} + +static int test_sss_nss_getnamebysid_update_acct_cb(void *pvt) +{ + errno_t ret; + struct sss_nss_test_ctx *ctx = talloc_get_type(pvt, struct sss_nss_test_ctx); + + testbysid_update.pw_shell = discard_const("/bin/ksh"); + ret = store_user(ctx, sss_nss_test_ctx->tctx->dom, + &testbysid_update, NULL, 0); + assert_int_equal(ret, EOK); + + return EOK; +} + +void test_sss_nss_getnamebysid_update(void **state) +{ + errno_t ret; + struct ldb_result *res; + struct sysdb_attrs *attrs; + const char *shell; + char *user_sid; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + user_sid = talloc_asprintf(attrs, "%s-123456", + sss_nss_test_ctx->tctx->dom->domain_id); + assert_non_null(user_sid); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, user_sid); + assert_int_equal(ret, EOK); + + /* Prime the cache with a valid but expired user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testbysid_update, attrs, 1); + assert_int_equal(ret, EOK); + + /* Mock client input */ + mock_input_sid(user_sid); + /* Mock client command */ + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYSID); + /* Call this function when user is updated by the mock DP request */ + mock_account_recv(0, 0, NULL, test_sss_nss_getnamebysid_update_acct_cb, + sss_nss_test_ctx); + /* Call this function to check what the responder returned to the client */ + set_cmd_cb(test_sss_nss_getnamebysid_update_check); + /* Mock output buffer */ + mock_fill_bysid(); + + /* Fire the command */ + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Check the user was updated in the cache */ + ret = get_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + testbysid_update.pw_name, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + + shell = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SHELL, NULL); + assert_string_equal(shell, "/bin/ksh"); +} + +struct passwd testbycert = { + .pw_name = discard_const("testcertuser"), + .pw_uid = 23456, + .pw_gid = 6890, + .pw_dir = discard_const("/home/testcertuser"), + .pw_gecos = discard_const("test cert user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct passwd testbycert2 = { + .pw_name = discard_const("testcertuser2"), + .pw_uid = 23457, + .pw_gid = 6890, + .pw_dir = discard_const("/home/testcertuser2"), + .pw_gecos = discard_const("test cert user2"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +#define TEST_TOKEN_CERT \ +"MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \ +"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA2MjMx" \ +"NjMyMDdaFw0xNzA2MjMxNjMyMDdaMDIxEjAQBgNVBAoMCUlQQS5ERVZFTDEcMBoG" \ +"A1UEAwwTaXBhLWRldmVsLmlwYS5kZXZlbDCCASIwDQYJKoZIhvcNAQEBBQADggEP" \ +"ADCCAQoCggEBALXUq56VlY+Z0aWLLpFAjFfbElPBXGQsbZb85J3cGyPjaMHC9wS+" \ +"wjB6Ve4HmQyPLx8hbINdDmbawMHYQvTScLYfsqLtj0Lqw20sUUmedk+Es5Oh9VHo" \ +"nd8MavYx25Du2u+T0iSgNIDikXguiwCmtAj8VC49ebbgITcjJGzMmiiuJkV3o93Y" \ +"vvYF0VjLGDQbQWOy7IxzYJeNVJnZWKo67CHdok6qOrm9rxQt81rzwV/mGLbCMUbr" \ +"+N4M8URtd7EmzaYZQmNm//s2owFrCYMxpLiURPj+URZVuB72504/Ix7X0HCbA/AV" \ +"26J27fPY5nc8DMwfhUDCbTqPH/JEjd3mvY8CAwEAAaOCASYwggEiMB8GA1UdIwQY" \ +"MBaAFJOq+KAQmPEnNp8Wok23eGTdE7aDMDsGCCsGAQUFBwEBBC8wLTArBggrBgEF" \ +"BQcwAYYfaHR0cDovL2lwYS1jYS5pcGEuZGV2ZWwvY2Evb2NzcDAOBgNVHQ8BAf8E" \ +"BAMCBPAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHQGA1UdHwRtMGsw" \ +"aaAxoC+GLWh0dHA6Ly9pcGEtY2EuaXBhLmRldmVsL2lwYS9jcmwvTWFzdGVyQ1JM" \ +"LmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRl" \ +"IEF1dGhvcml0eTAdBgNVHQ4EFgQUFaDNd5a53QGpaw5m63hnwXicMQ8wDQYJKoZI" \ +"hvcNAQELBQADggEBADH7Nj00qqGhGJeXJQAsepqSskz/wooqXh8vgVyb8SS4N0/c" \ +"0aQtVmY81xamlXE12ZFpwDX43d+EufBkwCUKFX/+8JFDd2doAyeJxv1xM22kKRpc" \ +"AqITPgMsa9ToGMWxjbVpc/X/5YfZixWPF0/eZUTotBj9oaR039UrhGfyN7OguF/G" \ +"rzmxtB5y4ZrMpcD/Oe90mkd9HY7sA/fB8OWOUgeRfQoh97HNS0UiDWsPtfxmjQG5" \ +"zotpoBIZmdH+ipYsu58HohHVlM9Wi5H4QmiiXl+Soldkq7eXYlafcmT7wv8+cKwz" \ +"Nz0Tm3+eYpFqRo3skr6QzXi525Jkg3r6r+kkhxU=" + +static int test_sss_nss_getnamebycert_check(uint32_t status, uint8_t *body, size_t blen) +{ + size_t rp = 2 * sizeof(uint32_t); /* num_results and reserved */ + uint32_t id_type; + const char *name; + + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&id_type, body + rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_UID); + + name = (const char *)body + rp; + assert_string_equal(name, testbycert.pw_name); + + return EOK; +} + +static int test_sss_nss_getlistbycert_check_exp(uint32_t status, uint8_t *body, + size_t blen, size_t exp) +{ + size_t rp = 0; + uint32_t id_type; + uint32_t num; + uint32_t reserved; + const char *name; + int found = 0; + const char *fq_name1 = "testcertuser@"TEST_DOM_NAME ; + const char *fq_name2 = "testcertuser2@"TEST_SUBDOM_NAME; + + assert_int_equal(status, EOK); + + /* num_results and reserved */ + SAFEALIGN_COPY_UINT32(&num, body + rp, &rp); + assert_int_equal(num, exp); + SAFEALIGN_COPY_UINT32(&reserved, body + rp, &rp); + assert_int_equal(reserved, 0); + + SAFEALIGN_COPY_UINT32(&id_type, body + rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_UID); + + name = (const char *)body + rp; + if (num == 1) { + assert_string_equal(name, fq_name1); + return EOK; + } + + rp += strlen(name) + 1; + if (strcmp(name, fq_name1) == 0) { + found = 1; + } else if (strcmp(name, fq_name2) == 0) { + found = 2; + } + assert_in_range(found, 1, 2); + + SAFEALIGN_COPY_UINT32(&id_type, body + rp, &rp); + assert_int_equal(id_type, SSS_ID_TYPE_UID); + + name = (const char *)body + rp; + if (found == 1) { + assert_string_equal(name, fq_name2); + } else { + assert_string_equal(name, fq_name1); + } + + + return EOK; +} + +static int test_sss_nss_getlistbycert_check_one(uint32_t status, uint8_t *body, + size_t blen) +{ + return test_sss_nss_getlistbycert_check_exp(status, body, blen, 1); +} + +static int test_sss_nss_getlistbycert_check_two(uint32_t status, uint8_t *body, + size_t blen) +{ + return test_sss_nss_getlistbycert_check_exp(status, body, blen, 2); +} + +static void test_sss_nss_getnamebycert(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + unsigned char *der = NULL; + size_t der_size; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + der = sss_base64_decode(sss_nss_test_ctx, TEST_TOKEN_CERT, &der_size); + assert_non_null(der); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + + /* Prime the cache with a valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testbycert, attrs, 0); + assert_int_equal(ret, EOK); + talloc_free(attrs); + + mock_input_cert(TEST_TOKEN_CERT); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYCERT); + mock_fill_bysid(); + + /* Query for that user, call a callback when command finishes */ + /* Should go straight to back end, without contacting DP */ + set_cmd_cb(test_sss_nss_getnamebycert_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYCERT, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getnamebycert_neg(void **state) +{ + errno_t ret; + + mock_input_cert(TEST_TOKEN_CERT); + mock_account_recv_simple(); + + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYCERT, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_int_equal(sss_nss_test_ctx->ncache_hits, 0); + + /* 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 + */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_cert(TEST_TOKEN_CERT); + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNAMEBYCERT, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); + /* Negative cache was hit this time */ + assert_int_equal(sss_nss_test_ctx->ncache_hits, 1); +} + +static void test_sss_nss_getlistbycert(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + unsigned char *der = NULL; + size_t der_size; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + der = sss_base64_decode(sss_nss_test_ctx, TEST_TOKEN_CERT, &der_size); + assert_non_null(der); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + + /* Prime the cache with a valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testbycert, attrs, 0); + assert_int_equal(ret, EOK); + talloc_free(attrs); + + mock_input_cert(TEST_TOKEN_CERT); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETLISTBYCERT); + mock_fill_bysid(); + + /* Query for that user, call a callback when command finishes */ + /* Should go straight to back end, without contacting DP. */ + /* If there is only a single user mapped the result will look like the */ + /* result of getnamebycert. */ + set_cmd_cb(test_sss_nss_getlistbycert_check_one); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static void test_sss_nss_getlistbycert_multi(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + unsigned char *der = NULL; + size_t der_size; + + der = sss_base64_decode(sss_nss_test_ctx, TEST_TOKEN_CERT, &der_size); + assert_non_null(der); + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + assert_int_equal(ret, EOK); + + /* Prime the cache with two valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testbycert, attrs, 0); + assert_int_equal(ret, EOK); + talloc_free(attrs); + + /* Looks like attrs is modified during store_user() makes sure we start + * with fresh data. */ + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->subdom, + &testbycert2, attrs, 0); + assert_int_equal(ret, EOK); + talloc_free(attrs); + + mock_input_cert(TEST_TOKEN_CERT); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETLISTBYCERT); + mock_fill_bysid(); + + /* Query for that user, call a callback when command finishes */ + /* Should go straight to back end, without contacting DP */ + set_cmd_cb(test_sss_nss_getlistbycert_check_two); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETLISTBYCERT, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +struct passwd sid_user = { + .pw_name = discard_const("testusersid"), + .pw_uid = 1234, + .pw_gid = 5678, + .pw_dir = discard_const("/home/testusersid"), + .pw_gecos = discard_const("test user"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct passwd sid_user_upg = { + .pw_name = discard_const("testusersidupg"), + .pw_uid = 5678, + .pw_gid = 5678, + .pw_dir = discard_const("/home/testusersidupg"), + .pw_gecos = discard_const("test user upg"), + .pw_shell = discard_const("/bin/sh"), + .pw_passwd = discard_const("*"), +}; + +struct group sid_user_group = { + .gr_name = discard_const("testusersidupg"), + .gr_gid = 5678, +}; + +struct group sid_group = { + .gr_name = discard_const("testgroupsid"), + .gr_gid = 5555, +}; + +static int test_sss_nss_getsidbyname_check(uint32_t status, + uint8_t *body, + size_t blen) +{ + const char *name; + enum sss_id_type type; + size_t rp = 2 * sizeof(uint32_t); + char *expected_result = sss_mock_ptr_type(char *); + + if (expected_result == NULL) { + assert_int_equal(status, EINVAL); + assert_int_equal(blen, 0); + } else { + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&type, body+rp, &rp); + + name = (char *) body+rp; + + assert_int_equal(type, SSS_ID_TYPE_UID); + assert_string_equal(name, expected_result); + } + + return EOK; +} + +static int test_sss_nss_getsidbygroupname_check(uint32_t status, + uint8_t *body, + size_t blen) +{ + const char *name; + enum sss_id_type type; + size_t rp = 2 * sizeof(uint32_t); + char *expected_result = sss_mock_ptr_type(char *); + + if (expected_result == NULL) { + assert_int_equal(status, EINVAL); + assert_int_equal(blen, 0); + } else { + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&type, body+rp, &rp); + + name = (char *) body+rp; + + assert_int_equal(type, SSS_ID_TYPE_GID); + assert_string_equal(name, expected_result); + } + + return EOK; +} + +void test_sss_nss_getsidbyname(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testuser_sid = "S-1-2-3-4"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testuser_sid); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testusersid"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_check, testuser_sid); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbyname_ipa_upg(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testuser_sid = "S-1-2-3-4"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testuser_sid); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user_upg, attrs, 0); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user_group, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testusersid"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_check, testuser_sid); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbyname_ipa_upg_manual(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testuser_sid = "S-1-2-3-4"; + const char *testgroup_sid = "S-1-2-3-5"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testuser_sid); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user_upg, attrs, 0); + assert_int_equal(ret, EOK); + + talloc_free(attrs); + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testgroup_sid); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user_group, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testusersid"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_check, testuser_sid); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbyusername_user(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testuser_sid = "S-1-2-3-4"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testuser_sid); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testusersid"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYUSERNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_check, testuser_sid); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYUSERNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbyusername_group(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testgroup_sid = "S-1-2-3-4"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testgroup_sid); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_group, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testgroupsid"); + mock_account_recv_simple(); + set_cmd_cb(NULL); + + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYUSERNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); +} + +void test_sss_nss_getsidbygroupname_group(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testgroup_sid = "S-1-2-3-4"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testgroup_sid); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_group, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testgroupsid"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYGROUPNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbygroupname_check, testgroup_sid); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbygroupname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYGROUPNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbygroupname_user(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testuser_sid = "S-1-2-3-4"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testuser_sid); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testusersid"); + mock_account_recv_simple(); + set_cmd_cb(NULL); + + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYGROUPNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); +} + +void test_sss_nss_getsidbyid(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testuser_sid = "S-1-2-3-4"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testuser_sid); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_id(sss_nss_test_ctx, 1234); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYID); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_check, testuser_sid); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbyuid(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testuser_sid = "S-1-2-3-4"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testuser_sid); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_id(sss_nss_test_ctx, 1234); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYUID); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_check, testuser_sid); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYUID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbygid_no_group(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testuser_sid = "S-1-2-3-4"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testuser_sid); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_id(sss_nss_test_ctx, 1234); + mock_account_recv_simple(); + set_cmd_cb(NULL); + + /* Query for that user, call a callback when command finishes */ + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYGID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT (because there is no such + * group) */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); +} + +static int test_sss_nss_getsidbyname_group_check(uint32_t status, + uint8_t *body, + size_t blen) +{ + const char *name; + enum sss_id_type type; + size_t rp = 2 * sizeof(uint32_t); + char *expected_result = sss_mock_ptr_type(char *); + + if (expected_result == NULL) { + assert_int_equal(status, EINVAL); + assert_int_equal(blen, 0); + } else { + assert_int_equal(status, EOK); + + SAFEALIGN_COPY_UINT32(&type, body+rp, &rp); + + name = (char *) body+rp; + + assert_int_equal(type, SSS_ID_TYPE_GID); + assert_string_equal(name, expected_result); + } + + return EOK; +} + +void test_sss_nss_getsidbyname_group(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testgroup_sid = "S-1-2-3-5"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testgroup_sid); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_group, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group("testgroupsid"); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_group_check, testgroup_sid); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_group_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbyid_group(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testgroup_sid = "S-1-2-3-5"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testgroup_sid); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_group, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_id(sss_nss_test_ctx, 5555); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYID); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_group_check, testgroup_sid); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_group_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbygid_group(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testgroup_sid = "S-1-2-3-5"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testgroup_sid); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_group, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_id(sss_nss_test_ctx, 5555); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYGID); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_group_check, testgroup_sid); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_group_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYGID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbyuid_no_user(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testgroup_sid = "S-1-2-3-5"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testgroup_sid); + assert_int_equal(ret, EOK); + + ret = store_group(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_group, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_id(sss_nss_test_ctx, 5555); + mock_account_recv_simple(); + set_cmd_cb(NULL); + + /* Query for that user, call a callback when command finishes */ + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYUID, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT (because there is no such + * user) */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); +} + +void test_sss_nss_getsidbyupn(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + const char *testuser_sid = "S-1-2-3-4"; + const char *testuser_upn = "testusersid@upndomain.test"; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, testuser_sid); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, testuser_upn); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &sid_user, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_upn(testuser_upn); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + will_return(test_sss_nss_getsidbyname_check, testuser_sid); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getsidbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getsidbyname_neg(void **state) +{ + errno_t ret; + + mock_input_user_or_group("testnosuchsid"); + mock_account_recv_simple(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(NULL); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with ENOENT (because there is no such SID */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, ENOENT); +} + +static int test_sss_nss_EINVAL_check(uint32_t status, uint8_t *body, size_t blen) +{ + assert_int_equal(status, EINVAL); + assert_int_equal(blen, 0); + + return EOK; +} + +#define RESET_TCTX do { \ + sss_nss_test_ctx->tctx->done = false; \ + sss_nss_test_ctx->tctx->error = EIO; \ +} while (0) + +void test_sss_nss_getpwnam_ex(void **state) +{ + errno_t ret; + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getpwnam_usr, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group_ex(true, "testuser", 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwnam_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use old input format, expect EINVAL */ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, "testuser"); + will_return(__wrap_sss_packet_get_body, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use unsupported flag combination, expect EINVAL */ + mock_input_user_or_group_ex(false, "testuser", + SSS_NSS_EX_FLAG_NO_CACHE + |SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_NO_CACHE, + * will cause a backend lookup -> mock_account_recv_simple() */ + mock_input_user_or_group_ex(true, "testuser", SSS_NSS_EX_FLAG_NO_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX); + mock_fill_user(); + mock_account_recv_simple(); + + set_cmd_cb(test_sss_nss_getpwnam_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */ + mock_input_user_or_group_ex(true, "testuser", + SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWNAM_EX); + mock_fill_user(); + + set_cmd_cb(test_sss_nss_getpwnam_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getpwuid_ex(void **state) +{ + errno_t ret; + uint32_t id = 101; + + /* Prime the cache with a valid user */ + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &getpwuid_usr, NULL, 0); + assert_int_equal(ret, EOK); + + mock_input_id_ex(sss_nss_test_ctx, id, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX); + mock_fill_user(); + + /* Query for that id, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getpwuid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use old input format, expect failure */ + mock_input_id(sss_nss_test_ctx, id); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use unsupported flag combination, expect EINVAL */ + mock_input_id_ex(sss_nss_test_ctx, id, SSS_NSS_EX_FLAG_NO_CACHE + |SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_NO_CACHE, + * will cause a backend lookup -> mock_account_recv_simple() */ + mock_input_id_ex(sss_nss_test_ctx, id, SSS_NSS_EX_FLAG_NO_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX); + mock_fill_user(); + mock_account_recv_simple(); + + set_cmd_cb(test_sss_nss_getpwuid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */ + mock_input_id_ex(sss_nss_test_ctx, id, SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETPWUID_EX); + mock_fill_user(); + + set_cmd_cb(test_sss_nss_getpwuid_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETPWUID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getgrnam_ex_no_members(void **state) +{ + errno_t ret; + + /* Test group is still in the cache */ + + mock_input_user_or_group_ex(true, getgrnam_no_members.gr_name, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_no_members_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use old input format, expect failure */ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, "testgroup"); + will_return(__wrap_sss_packet_get_body, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use unsupported flag combination, expect EINVAL */ + mock_input_user_or_group_ex(false, getgrnam_no_members.gr_name, + SSS_NSS_EX_FLAG_NO_CACHE + |SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_NO_CACHE, + * will cause a backend lookup -> mock_account_recv_simple() */ + mock_input_user_or_group_ex(true, getgrnam_no_members.gr_name, + SSS_NSS_EX_FLAG_NO_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + mock_account_recv_simple(); + + set_cmd_cb(test_sss_nss_getgrnam_no_members_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */ + mock_input_user_or_group_ex(true, getgrnam_no_members.gr_name, + SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRNAM_EX); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_sss_nss_getgrnam_no_members_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRNAM_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getgrgid_ex_no_members(void **state) +{ + errno_t ret; + + /* Test group is still in the cache */ + + mock_input_id_ex(sss_nss_test_ctx, getgrnam_no_members.gr_gid, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRGID_EX); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + mock_account_recv_simple(); + + /* Query for that group, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getgrnam_no_members_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use old input format, expect failure */ + mock_input_id(sss_nss_test_ctx, getgrnam_no_members.gr_gid); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRGID_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use unsupported flag combination, expect EINVAL */ + mock_input_id_ex(sss_nss_test_ctx, getgrnam_no_members.gr_gid, + SSS_NSS_EX_FLAG_NO_CACHE + |SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRGID_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_NO_CACHE, + * will cause a backend lookup -> mock_account_recv_simple() */ + mock_input_id_ex(sss_nss_test_ctx, getgrnam_no_members.gr_gid, + SSS_NSS_EX_FLAG_NO_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETGRGID_EX); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + mock_account_recv_simple(); + + set_cmd_cb(test_sss_nss_getgrnam_no_members_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */ + mock_input_id_ex(sss_nss_test_ctx, getgrnam_no_members.gr_gid, + SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + + set_cmd_cb(test_sss_nss_getgrnam_no_members_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETGRGID_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_initgroups_ex(void **state) +{ + errno_t ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(sss_nss_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, + time(NULL) + 300); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, "upninitgr@upndomain.test"); + assert_int_equal(ret, EOK); + + ret = store_user(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + &testinitgr_usr, attrs, 0); + assert_int_equal(ret, EOK); + + mock_input_user_or_group_ex(true, "testinitgr", 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX); + mock_fill_user(); + + /* Query for that user, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_initgr_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use old input format, expect failure */ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, "testinitgr"); + will_return(__wrap_sss_packet_get_body, 0); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use unsupported flag combination, expect EINVAL */ + mock_input_user_or_group_ex(false, "testinitgr", + SSS_NSS_EX_FLAG_NO_CACHE + |SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX); + + set_cmd_cb(test_sss_nss_EINVAL_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_NO_CACHE, + * will cause a backend lookup -> mock_account_recv_simple() */ + mock_input_user_or_group_ex(true, "testinitgr", + SSS_NSS_EX_FLAG_NO_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX); + mock_fill_user(); + mock_account_recv_simple(); + + set_cmd_cb(test_sss_nss_initgr_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + RESET_TCTX; + + /* Use flag SSS_NSS_EX_FLAG_INVALIDATE_CACHE */ + mock_input_user_or_group_ex(true, "testinitgr", + SSS_NSS_EX_FLAG_INVALIDATE_CACHE); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_INITGR_EX); + mock_fill_user(); + + set_cmd_cb(test_sss_nss_initgr_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_INITGR_EX, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +const char *test_hostent_aliases[] = { + "testhost_alias1", + "testhost_alias2", + "testhost_alias3", + NULL +}; + +const char *test_hostent_addrlist[] = { + "1.2.3.4", + "9.8.7.6", + "2001:db8:1234::", + "2001:db8::1234", + NULL +}; + +struct hostent test_hostent = { + .h_name = discard_const("testhost"), + .h_aliases = discard_const(test_hostent_aliases), + .h_addrtype = AF_INET, + .h_length = 4, + .h_addr_list = discard_const(test_hostent_addrlist), +}; + +static void mock_input_hostbyname(const char *hostname) +{ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, hostname); + will_return(__wrap_sss_packet_get_body, 0); + mock_parse_inp(hostname, NULL, EOK); +} + +static int parse_host_packet(int af, uint8_t *body, size_t blen, struct hostent *hent) +{ + size_t rp = 2 * sizeof(uint32_t); /* Number of results and reserved */ + unsigned int num_aliases; + unsigned int num_addresses; + unsigned int i; + + SAFEALIGN_COPY_UINT32(&num_aliases, body+rp, &rp); + SAFEALIGN_COPY_UINT32(&num_addresses, body+rp, &rp); + + hent->h_addrtype = af; + hent->h_length = (af == AF_INET6 ? 16 : 4); + + hent->h_name = talloc_strdup(sss_nss_test_ctx, (char *) body+rp); + rp += strlen(hent->h_name) + 1; + + if (num_aliases > 0) { + hent->h_aliases = talloc_zero_array(sss_nss_test_ctx, char *, num_aliases + 1); + for (i=0; i<num_aliases; i++) { + hent->h_aliases[i] = talloc_strdup(hent->h_aliases, (char *) body+rp); + hent->h_aliases[i + 1] = NULL; + rp += strlen(hent->h_aliases[i]) + 1; + } + } + + if (num_addresses > 0) { + hent->h_addr_list = talloc_zero_array(sss_nss_test_ctx, char *, num_addresses + 1); + for (i=0; i<num_addresses; i++) { + hent->h_addr_list[i] = talloc_strdup(hent->h_addr_list, (char *) body+rp); + hent->h_addr_list[i + 1] = NULL; + rp += strlen(hent->h_addr_list[i]) + 1; + } + } + + /* Make sure we exactly matched the end of the packet */ + if (rp != blen) { + return EINVAL; + } + + return EOK; +} + +static void assert_host_equal(int af, struct hostent *ref, struct hostent *b) +{ + unsigned int i; + + assert_string_equal(ref->h_name, b->h_name); + assert_int_equal(af, b->h_addrtype); + assert_int_equal(af == AF_INET6 ? 16 : 4, b->h_length); + for (i=0; ref->h_aliases[i] != NULL; i++) { + assert_non_null(b->h_aliases[i]); + assert_string_equal(ref->h_aliases[i], b->h_aliases[i]); + } + assert_null(b->h_aliases[i]); + + for (i=0; ref->h_addr_list[i] != NULL; i++) { + assert_string_equal(ref->h_addr_list[i], b->h_addr_list[i]); + } + assert_null(b->h_addr_list[i]); +} + +static int test_sss_nss_gethostbyname_check(uint32_t status, uint8_t *body, size_t blen) +{ + struct hostent hostent = { 0 }; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_host_packet(AF_INET, body, blen, &hostent); + assert_int_equal(ret, EOK); + + assert_host_equal(AF_INET, &test_hostent, &hostent); + + return EOK; +} + +static int test_sss_nss_gethostbyaddr_v6_check(uint32_t status, uint8_t *body, size_t blen) +{ + struct hostent hostent = { 0 }; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_host_packet(AF_INET6, body, blen, &hostent); + assert_int_equal(ret, EOK); + + assert_host_equal(AF_INET6, &test_hostent, &hostent); + + return EOK; +} + +void test_sss_nss_gethostbyname(void **state) +{ + errno_t ret; + + mock_input_hostbyname("testhost"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETHOSTBYNAME); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 9); + + /* Query for that host, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_gethostbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETHOSTBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Test search by aliases */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_hostbyname("testhost_alias1"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETHOSTBYNAME); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 9); + + set_cmd_cb(test_sss_nss_gethostbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETHOSTBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Test search by aliases */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_hostbyname("testhost_alias2"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETHOSTBYNAME); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 9); + + set_cmd_cb(test_sss_nss_gethostbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETHOSTBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Test search by aliases */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_hostbyname("testhost_alias3"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETHOSTBYNAME); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 9); + + set_cmd_cb(test_sss_nss_gethostbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETHOSTBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static void mock_input_netaddr(TALLOC_CTX *mem_ctx, int af, const char *addrstr) +{ + uint8_t *body; + size_t blen; + size_t addrlen; + errno_t ret; + char addr[16]; + + ret = inet_pton(af, addrstr, addr); + assert_int_equal(ret, 1); + + addrlen = (af == AF_INET6 ? 16 : 4); + blen = sizeof(uint32_t) * 2 + addrlen; + + body = talloc_zero_array(mem_ctx, uint8_t, blen); + if (body == NULL) { + return; + } + + SAFEALIGN_SETMEM_UINT32(body, af, NULL); + SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), addrlen, NULL); + SAFEALIGN_SETMEM_STRING(body + sizeof(uint32_t) * 2, addr, addrlen, NULL); + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, body); + will_return(__wrap_sss_packet_get_body, blen); +} + +void test_sss_nss_gethostbyaddr(void **state) +{ + errno_t ret; + + /* Host stored by previous tests */ + mock_input_netaddr(sss_nss_test_ctx, AF_INET, "1.2.3.4"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETHOSTBYADDR); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 9); + + /* Query for that host, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_gethostbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETHOSTBYADDR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Test search by aliases */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_netaddr(sss_nss_test_ctx, AF_INET, "9.8.7.6"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETHOSTBYADDR); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 9); + + /* Query for that host, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_gethostbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETHOSTBYADDR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Test search by aliases */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_netaddr(sss_nss_test_ctx, AF_INET6, "2001:DB8:1234:0::0000"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETHOSTBYADDR); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 9); + + /* Query for that host, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_gethostbyaddr_v6_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETHOSTBYADDR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Test search by aliases */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_netaddr(sss_nss_test_ctx, AF_INET6, "2001:DB8:0000::1234"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETHOSTBYADDR); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 9); + + /* Query for that host, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_gethostbyaddr_v6_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETHOSTBYADDR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int sss_nss_host_test_setup(void **state) +{ + const char **aliases = NULL; + const char **addrs = NULL; + errno_t ret; + unsigned int i; + + sss_nss_test_setup(state); + + /* sysdb_host_add expects a talloc_array of aliases and addresses */ + for (i = 0; test_hostent.h_aliases[i]; i++) { + aliases = talloc_realloc(sss_nss_test_ctx, aliases, const char *, i + 2); + assert_non_null(aliases); + + aliases[i] = talloc_strdup(aliases, test_hostent.h_aliases[i]); + assert_non_null(aliases[i]); + + aliases[i + 1] = NULL; + } + + for (i = 0; test_hostent.h_addr_list[i]; i++) { + addrs = talloc_realloc(sss_nss_test_ctx, addrs, const char *, i + 2); + assert_non_null(addrs); + + addrs[i] = talloc_strdup(addrs, test_hostent.h_addr_list[i]); + assert_non_null(addrs[i]); + + addrs[i + 1] = NULL; + } + + ret = sysdb_host_add(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + test_hostent.h_name, aliases, addrs, + NULL); + assert_int_equal(ret, EOK); + + return 0; +} + +static int sss_nss_host_test_teardown(void **state) +{ + errno_t ret; + + ret = sysdb_host_delete(sss_nss_test_ctx->tctx->dom, + test_hostent.h_name, NULL); + assert_int_equal(ret, EOK); + + return sss_nss_test_teardown(state); +} + +const char *test_netent_aliases[] = { + "test_network_alias1", + "test_network_alias2", + "test_network_alias3", + NULL +}; + +struct netent test_netent = { + .n_name = discard_const("test_network"), + .n_aliases = discard_const(test_netent_aliases), + .n_addrtype = AF_INET, +#if (__BYTE_ORDER == __LITTLE_ENDIAN) + .n_net = 0x04030201 /* 1.2.3.4 */ +#elif (__BYTE_ORDER == __BIG_ENDIAN) + .n_net = 0x01020304 /* 1.2.3.4 */ +#else + #error "unknow endianess" +#endif +}; + +static void mock_input_netbyname(const char *name) +{ + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, name); + will_return(__wrap_sss_packet_get_body, 0); + mock_parse_inp(name, NULL, EOK); +} + +static int parse_network_packet(int type, uint8_t *body, size_t blen, + struct netent *netent) +{ + size_t rp = 2 * sizeof(uint32_t); /* Number of results and reserved */ + char *address; + unsigned int num_aliases; + unsigned int i; + int ret; + + SAFEALIGN_COPY_UINT32(&num_aliases, body+rp, &rp); + + netent->n_addrtype = type; + + netent->n_name = talloc_strdup(sss_nss_test_ctx, (char *) body+rp); + rp += strlen(netent->n_name) + 1; + + address = (char *) body+rp; + ret = inet_pton(AF_INET, address, &netent->n_net); + if (ret != 1) { + return EINVAL; + } + rp += strlen(address) + 1; + + if (num_aliases > 0) { + netent->n_aliases = talloc_zero_array(sss_nss_test_ctx, char *, + num_aliases + 1); + for (i=0; i<num_aliases; i++) { + netent->n_aliases[i] = talloc_strdup(netent->n_aliases, + (char *) body+rp); + netent->n_aliases[i + 1] = NULL; + rp += strlen(netent->n_aliases[i]) + 1; + } + } + + /* Make sure we exactly matched the end of the packet */ + if (rp != blen) { + return EINVAL; + } + + return EOK; +} + +static void assert_network_equal(int type, struct netent *ref, struct netent *b) +{ + unsigned int i; + + assert_string_equal(ref->n_name, b->n_name); + assert_int_equal(type, b->n_addrtype); + assert_int_equal(ref->n_addrtype, b->n_addrtype); + assert_int_equal(ref->n_net, b->n_net); + for (i=0; ref->n_aliases[i] != NULL; i++) { + assert_non_null(b->n_aliases[i]); + assert_string_equal(ref->n_aliases[i], b->n_aliases[i]); + } + assert_null(b->n_aliases[i]); +} + +static int test_sss_nss_getnetbyname_check(uint32_t status, uint8_t *body, size_t blen) +{ + struct netent netent = { 0 }; + errno_t ret; + + assert_int_equal(status, EOK); + + ret = parse_network_packet(AF_INET, body, blen, &netent); + assert_int_equal(ret, EOK); + + assert_network_equal(AF_INET, &test_netent, &netent); + + return EOK; +} + +void test_sss_nss_getnetbyname(void **state) +{ + errno_t ret; + + mock_input_netbyname("test_network"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNETBYNAME); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 5); + + /* Query for that network, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getnetbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNETBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Test search by aliases */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_netbyname("test_network_alias1"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNETBYNAME); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 5); + + set_cmd_cb(test_sss_nss_getnetbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNETBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Test search by aliases */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_netbyname("test_network_alias2"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNETBYNAME); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 5); + + set_cmd_cb(test_sss_nss_getnetbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNETBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); + + /* Test search by aliases */ + sss_nss_test_ctx->tctx->done = false; + + mock_input_netbyname("test_network_alias3"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNETBYNAME); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 5); + + set_cmd_cb(test_sss_nss_getnetbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNETBYNAME, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_sss_nss_getnetbyaddr(void **state) +{ + errno_t ret; + + mock_input_netaddr(sss_nss_test_ctx, AF_INET, "1.2.3.4"); + mock_resolver_recv_simple(); + will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNETBYADDR); + will_return_count(__wrap_sss_packet_get_body, WRAP_CALL_REAL, 5); + + /* Query for that network, call a callback when command finishes */ + set_cmd_cb(test_sss_nss_getnetbyname_check); + ret = sss_cmd_execute(sss_nss_test_ctx->cctx, SSS_NSS_GETNETBYADDR, + sss_nss_test_ctx->sss_nss_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(sss_nss_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int sss_nss_network_test_setup(void **state) +{ + const char **aliases = NULL; + errno_t ret; + unsigned int i; + + sss_nss_test_setup(state); + + /* sysdb_ipnetwork_add expects a talloc_array of aliases */ + for (i = 0; test_netent.n_aliases[i]; i++) { + aliases = talloc_realloc(sss_nss_test_ctx, aliases, const char *, i + 2); + assert_non_null(aliases); + + aliases[i] = talloc_strdup(aliases, test_netent.n_aliases[i]); + assert_non_null(aliases[i]); + + aliases[i + 1] = NULL; + } + + ret = sysdb_ipnetwork_add(sss_nss_test_ctx, sss_nss_test_ctx->tctx->dom, + test_netent.n_name, aliases, "1.2.3.4", + NULL); + assert_int_equal(ret, EOK); + + return 0; +} + +static int sss_nss_network_test_teardown(void **state) +{ + errno_t ret; + + ret = sysdb_ipnetwork_delete(sss_nss_test_ctx->tctx->dom, + test_netent.n_name, NULL); + assert_int_equal(ret, EOK); + + return sss_nss_test_teardown(state); +} + + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwuid, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_neg, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwuid_neg, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_search, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwuid_search, + sss_nss_test_setup, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_update, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwuid_update, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_fqdn, + sss_nss_fqdn_test_setup, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_fqdn_fancy, + sss_nss_fqdn_fancy_test_setup, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_space, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_space_sub, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_space_sub_query, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_no_members, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_members, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_members_fqdn, + sss_nss_fqdn_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_members_subdom, + sss_nss_subdom_test_setup, + sss_nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_members_subdom_nonfqnames, + sss_nss_subdom_test_setup_nonfqnames, + sss_nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_mix_dom, + sss_nss_subdom_test_setup, + sss_nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_mix_dom_nonfqnames, + sss_nss_subdom_test_setup_nonfqnames, + sss_nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_mix_dom_fqdn, + sss_nss_subdom_test_setup, + sss_nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_mix_dom_fqdn_nonfqnames, + sss_nss_subdom_test_setup_nonfqnames, + sss_nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_mix_subdom, + sss_nss_subdom_test_setup, + sss_nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_mix_subdom_nonfqnames, + sss_nss_subdom_test_setup_nonfqnames, + sss_nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_space, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_space_sub, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_well_known_getnamebysid, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_well_known_getnamebysid_special, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_well_known_getnamebysid_non_existing, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_well_known_getidbysid_failure, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_well_known_getsidbyname, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_well_known_getsidbyname_nonexisting, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_well_known_getsidbyname_special, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getorigbyname, + sss_nss_test_setup, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getorigbyname_extra_attrs, + sss_nss_test_setup_extra_attr, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getorigbyname_multi_value_attrs, + sss_nss_test_setup_extra_attr, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getorigbyname, + sss_nss_test_setup, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getorigbyusername, + sss_nss_test_setup, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getorigbygroupname, + sss_nss_test_setup, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getorigbyname_dup, + sss_nss_test_setup, + sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_upn, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_upn_same_domain, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_upn_neg, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_initgroups, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_initgr_neg, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_initgr_search, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_initgr_update, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_initgr_update_two_expire_attributes, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_initgroups_upn, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_initgr_neg_upn, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getnamebysid, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getnamebysid_neg, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getnamebysid_update, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getnamebycert_neg, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getnamebycert, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getlistbycert, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getlistbycert_multi, + sss_nss_subdom_test_setup, + sss_nss_subdom_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyname, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyname_ipa_upg, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyusername_user, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyusername_group, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbygroupname_user, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbygroupname_group, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyid, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyuid, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbygid_no_group, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyname_group, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyid_group, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbygid_group, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyuid_no_user, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyupn, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyname_neg, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getsidbyname_ipa_upg_manual, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwnam_ex, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getpwuid_ex, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrnam_ex_no_members, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getgrgid_ex_no_members, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_initgroups_ex, + sss_nss_test_setup, sss_nss_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_gethostbyname, + sss_nss_host_test_setup, + sss_nss_host_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_gethostbyaddr, + sss_nss_host_test_setup, + sss_nss_host_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getnetbyname, + sss_nss_network_test_setup, + sss_nss_network_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_nss_getnetbyaddr, + sss_nss_network_test_setup, + sss_nss_network_test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + + return rv; +} diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c new file mode 100644 index 0000000..faa4343 --- /dev/null +++ b/src/tests/cmocka/test_pam_srv.c @@ -0,0 +1,4790 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests: PAM responder tests + + 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 <security/pam_modules.h> +#include <popt.h> +#include <stdlib.h> /* putenv */ + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" +#include "responder/common/responder_packet.h" +#include "responder/common/negcache.h" +#include "responder/pam/pamsrv.h" +#include "responder/pam/pam_helpers.h" +#include "sss_client/pam_message.h" +#include "sss_client/sss_cli.h" +#include "confdb/confdb.h" +#ifdef BUILD_PASSKEY +#include "src/responder/pam/pamsrv_passkey.h" +#include "db/sysdb_passkey_user_verification.h" +#endif + +#include "util/crypto/sss_crypto.h" + +#ifdef HAVE_TEST_CA +#include "tests/test_CA/SSSD_test_cert_x509_0001.h" +#include "tests/test_CA/SSSD_test_cert_x509_0002.h" +#include "tests/test_CA/SSSD_test_cert_x509_0005.h" +#include "tests/test_CA/SSSD_test_cert_x509_0006.h" +#include "tests/test_CA/SSSD_test_cert_x509_0007.h" +#include "tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_x509_0001.h" +#include "tests/test_ECC_CA/SSSD_test_ECC_cert_x509_0001.h" +#else +#define SSSD_TEST_CERT_0001 "" +#define SSSD_TEST_CERT_0002 "" +#define SSSD_TEST_CERT_0005 "" +#define SSSD_TEST_CERT_0006 "" +#define SSSD_TEST_CERT_0007 "" +#define SSSD_TEST_INTERMEDIATE_CA_CERT_0001 "" +#define SSSD_TEST_ECC_CERT_0001 "" +#endif + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_pam_conf.ldb" +#define TEST_DOM_NAME "pam_test" +#define TEST_SUBDOM_NAME "test.subdomain" +#define TEST_ID_PROVIDER "ldap" + +#define CA_DB ABS_BUILD_DIR"/src/tests/test_CA/SSSD_test_CA.pem" +#define INTERMEDIATE_CA_DB \ + ABS_BUILD_DIR"/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA.pem" +#define INTERMEDIATE_FULL_CA_DB \ + ABS_BUILD_DIR"/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_full_db.pem" +#define ECC_CA_DB ABS_BUILD_DIR"/src/tests/test_ECC_CA/SSSD_test_ECC_CA.pem" + +#define TEST_TOKEN_NAME "SSSD Test Token" +#define TEST_TOKEN2_NAME "SSSD Test Token Number 2" +#define TEST_KEY_ID "C554C9F82C2A9D58B70921C143304153A8A42F17" +#define TEST_LABEL "SSSD test cert 0001" +#define TEST_MODULE_NAME SOFTHSM2_PATH +#define TEST_PROMPT "SSSD test cert 0001\nCN=SSSD test cert 0001,OU=SSSD test,O=SSSD" +#define TEST2_PROMPT "SSSD test cert 0002\nCN=SSSD test cert 0002,OU=SSSD test,O=SSSD" +#define TEST5_PROMPT "SSSD test cert 0005\nCN=SSSD test cert 0005,OU=SSSD test,O=SSSD" + +#define TEST2_KEY_ID "5405842D56CF31F0BB025A695C5F3E907051C5B9" +#define TEST2_LABEL "SSSD test cert 0002" +#define TEST5_KEY_ID "1195833C424AB00297F582FC43FFFFAB47A64CC9" +#define TEST5_LABEL "SSSD test cert 0005" + +#define SSSD_TEST_PASSKEY \ + "passkey:zO7lzqHPkVgsWkMTuJ17E+9OTcPtYUZJFHDs3xPSDgjcsHp/yLHkiRRNJ2IMU278" \ + "wdzGuHmSI4rOnyZ0VcJ/kA==,MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEKhSQWMPgAU" \ + "cz4d7Fjz2hZK7QUlnAttuEW5XrxD06VBaQvIRYJT7e6wM+vFU4z+uQgU9B5ERbgMiBVe99rB" \ + "L9w==" + +#define SSSD_TEST_PASSKEY_PK \ + "zO7lzqHPkVgsWkMTuJ17E+9OTcPtYUZJFHDs3xPSDgjcsHp/yLHkiRRNJ2IMU278" \ + "wdzGuHmSI4rOnyZ0VcJ/kA==" + +#define SSSD_TEST_PASSKEY_KEY_HANDLE \ + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEKhSQWMPgAUcz4d7Fjz2hZK7QUlnAttuEW5Xr" \ + "xD06VBaQvIRYJT7e6wM+vFU4z+uQgU9B5ERbgMiBVe99rBL9w==" + +#define SSSD_TEST_PUBKEY \ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCa+l8uZ6Q5G58PVMe1na7NrOMTzo2wOZfFwo" \ + "0fM3RbvfAdlz/wsGwln2+EXA19FiXu/nNj4EwYGP9hymKuYaXzpq40k0VbhEL1v/qzXQvuKZgN" \ + "x42vxi7NITaaAXuYj8OZQsZTvv+xgkREZmhQ6YqEjTJ0JzpD9fj8Gf8Mgn8pdsb/ZODLMAwEKt" \ + "Q2DaWqH5jCqzoGEJlRl+kRbnrHc+RQrmj7NnY1voEJNrmzCyJFH5awZyBl/ZdbvpnwCKnVEleB" \ + "FULrOIfJ9lc/QMmURCMa6RfW5CFrxdtjUwiIxfMiHe+zUY5T9L0Q6FWnlfNz/63Xdcrw1Gc90O" \ + "CZKcqf/4P9N5flGSGSfiO5fD8gCCJ0c3WhxSVMREDP3ibKDsz8yhw2OuyGcfRo4nnchxy9G703" \ + "1m2t9rUXc12eS1EKGJiPiT9IuTQ9nCG2PslkqR+KUMiYoS9MqTsAj9HhuTMkFhcYFyufxFmt/S" \ + "4rIqVwmP8lY4GwwJwOnZwNLj/I2HwC+pk= testuser@fedora.test.local" + +int no_cleanup; + +static char CACHED_AUTH_TIMEOUT_STR[] = "4"; +static const int CACHED_AUTH_TIMEOUT = 4; + +struct pam_test_ctx { + struct sss_test_ctx *tctx; + struct sss_domain_info *subdom; + + struct resp_ctx *rctx; + struct cli_ctx *cctx; + struct sss_cmd_table *pam_cmds; + struct pam_ctx *pctx; + + int ncache_hits; + int exp_pam_status; + enum prompt_config_type exp_prompt_config_type; + const char *exp_touch_prompt; + struct pam_data *pd; + bool provider_contacted; + + const char *pam_user_fqdn; + const char *wrong_user_fqdn; + int child_status; +}; + +/* Must be global because it is needed in some wrappers */ +struct pam_test_ctx *pam_test_ctx; + +struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx) +{ + struct pam_ctx *pctx; + errno_t ret; + + pctx = talloc_zero(mem_ctx, struct pam_ctx); + assert_non_null(pctx); + + ret = sss_hash_create(pctx, 10, &pctx->id_table); + assert_int_equal(ret, EOK); + + /* Two NULLs so that tests can just assign a const to the first slot + * should they need it. The code iterates until first NULL anyway + */ + pctx->app_services = talloc_zero_array(pctx, char *, 2); + if (pctx->app_services == NULL) { + talloc_free(pctx); + return NULL; + } + + ret = p11_refresh_certmap_ctx(pctx, NULL); + assert_int_equal(ret, 0); + + pctx->initgroups_scheme = PAM_INITGR_NO_SESSION; + + return pctx; +} + +static int add_confdb_params(struct sss_test_conf_param params[], + struct confdb_ctx *cdb, const char *section) +{ + const char *val[2]; + int ret; + + val[1] = NULL; + + for (int i = 0; params[i].key; i++) { + val[0] = params[i].value; + ret = confdb_add_param(cdb, true, section, params[i].key, val); + assert_int_equal(ret, EOK); + } + + return EOK; +} + +static int add_pam_params(struct sss_test_conf_param pam_params[], + struct confdb_ctx *cdb) +{ + return add_confdb_params(pam_params, cdb, CONFDB_PAM_CONF_ENTRY); +} + +static int add_monitor_params(struct sss_test_conf_param monitor_params[], + struct confdb_ctx *cdb) +{ + return add_confdb_params(monitor_params, cdb, CONFDB_MONITOR_CONF_ENTRY); +} + +void test_pam_setup(struct sss_test_conf_param dom_params[], + struct sss_test_conf_param pam_params[], + struct sss_test_conf_param monitor_params[], + void **state) +{ + struct cli_protocol *prctx; + errno_t ret; + + pam_test_ctx = talloc_zero(NULL, struct pam_test_ctx); + assert_non_null(pam_test_ctx); + + test_dom_suite_setup(TESTS_PATH); + pam_test_ctx->tctx = create_dom_test_ctx(pam_test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, dom_params); + assert_non_null(pam_test_ctx->tctx); + + pam_test_ctx->pam_cmds = get_pam_cmds(); + assert_non_null(pam_test_ctx->pam_cmds); + pam_test_ctx->tctx->dom->dns_name = talloc_strdup(pam_test_ctx, TEST_DOM_NAME); + + /* FIXME - perhaps this should be folded into sssd_domain_init or strictly + * used together + */ + ret = sss_names_init(pam_test_ctx, pam_test_ctx->tctx->confdb, + TEST_DOM_NAME, &pam_test_ctx->tctx->dom->names); + assert_int_equal(ret, EOK); + + /* Initialize the PAM responder */ + pam_test_ctx->pctx = mock_pctx(pam_test_ctx); + assert_non_null(pam_test_ctx->pctx); + + pam_test_ctx->rctx = mock_rctx(pam_test_ctx, pam_test_ctx->tctx->ev, + pam_test_ctx->tctx->dom, pam_test_ctx->pctx); + assert_non_null(pam_test_ctx->rctx); + pam_test_ctx->rctx->cdb = pam_test_ctx->tctx->confdb; + pam_test_ctx->pctx->rctx = pam_test_ctx->rctx; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + /* Create client context */ + pam_test_ctx->cctx = mock_cctx(pam_test_ctx, pam_test_ctx->rctx); + assert_non_null(pam_test_ctx->cctx); + pam_test_ctx->cctx->ev = pam_test_ctx->tctx->ev; + + prctx = mock_prctx(pam_test_ctx->cctx); + assert_non_null(prctx); + pam_test_ctx->cctx->protocol_ctx = prctx; + prctx->cli_protocol_version = register_cli_protocol_version(); + + pam_test_ctx->pd = create_pam_data(pam_test_ctx); + assert_non_null(pam_test_ctx->pd); +} + +static void pam_test_setup_common(void) +{ + errno_t ret; + time_t now; + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_one.conf")); + + pam_test_ctx->pam_user_fqdn = \ + sss_create_internal_fqname(pam_test_ctx, + "pamuser", + pam_test_ctx->tctx->dom->name); + assert_non_null(pam_test_ctx->pam_user_fqdn); + + pam_test_ctx->wrong_user_fqdn = \ + sss_create_internal_fqname(pam_test_ctx, + "wronguser", + pam_test_ctx->tctx->dom->name); + assert_non_null(pam_test_ctx->wrong_user_fqdn); + + /* integer values cannot be set by pam_params, since we are expecting that + * the PAM id cache stay valid during a test we have to make sure the + * timeout is long enough that even a run e.g. delayed by running with + * valgrind can pass. */ + pam_test_ctx->pctx->id_timeout = 60; + + now = time(NULL); + /* Prime the cache with a valid user */ + ret = sysdb_add_user(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + 123, 456, "pam user", + "/home/pamuser", "/bin/sh", NULL, + NULL, 300, now); + assert_int_equal(ret, EOK); + ret = sysdb_set_initgr_expire_timestamp(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn); + assert_int_equal(ret, EOK); + + /* Add entry to the initgr cache to make sure no initgr request is sent to + * the backend */ + ret = pam_initgr_cache_set(pam_test_ctx->pctx->rctx->ev, + pam_test_ctx->pctx->id_table, + discard_const("pamuser"), + pam_test_ctx->pctx->id_timeout); + assert_int_equal(ret, EOK); + + /* Prime the cache with a user for wrong matches */ + ret = sysdb_add_user(pam_test_ctx->tctx->dom, + pam_test_ctx->wrong_user_fqdn, + 321, 654, "wrong user", + "/home/wronguser", "/bin/sh", NULL, + NULL, 300, now); + assert_int_equal(ret, EOK); + ret = sysdb_set_initgr_expire_timestamp(pam_test_ctx->tctx->dom, + pam_test_ctx->wrong_user_fqdn); + assert_int_equal(ret, EOK); + + /* Add entry to the initgr cache to make sure no initgr request is sent to + * the backend */ + ret = pam_initgr_cache_set(pam_test_ctx->pctx->rctx->ev, + pam_test_ctx->pctx->id_table, + discard_const("wronguser"), + pam_test_ctx->pctx->id_timeout); + assert_int_equal(ret, EOK); +} + +static int pam_test_setup(void **state) +{ + struct sss_test_conf_param dom_params[] = { + { "enumerate", "false" }, + { "cache_credentials", "true" }, + { "entry_cache_timeout", "300" }, + { "local_auth_policy", "enable:smartcard" }, /* Needed to allow local sc auth */ + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_P11_URI, "pkcs11:manufacturer=SoftHSM%20project" }, + { "p11_child_timeout", "30" }, + { "pam_cert_verification", NULL }, + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "no_ocsp"}, + { NULL, NULL }, /* Sentinel */ + }; + + test_pam_setup(dom_params, pam_params, monitor_params, state); + + pam_test_setup_common(); + return 0; +} + +#ifdef BUILD_PASSKEY +static int pam_test_setup_passkey(void **state) +{ + struct sss_test_conf_param dom_params[] = { + { "enumerate", "false" }, + { "cache_credentials", "true" }, + { "entry_cache_timeout", "300" }, + { "local_auth_policy", "enable:passkey" }, /* Needed to allow local passkey auth */ + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_P11_URI, "pkcs11:manufacturer=SoftHSM%20project" }, + { "p11_child_timeout", "30" }, + { "pam_cert_verification", NULL }, + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "no_ocsp"}, + { NULL, NULL }, /* Sentinel */ + }; + + test_pam_setup(dom_params, pam_params, monitor_params, state); + + pam_test_setup_common(); + return 0; +} +#endif + +#ifdef HAVE_TEST_CA +static int pam_test_setup_no_verification(void **state) +{ + struct sss_test_conf_param dom_params[] = { + { "enumerate", "false" }, + { "cache_credentials", "true" }, + { "local_auth_policy", "enable:smartcard" }, /* Needed to allow local sc auth */ + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_P11_URI, "pkcs11:manufacturer=SoftHSM%20project" }, + { "p11_child_timeout", "30" }, + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "no_verification" }, + { NULL, NULL }, /* Sentinel */ + }; + + test_pam_setup(dom_params, pam_params, monitor_params, state); + + pam_test_setup_common(); + return 0; +} + +static int pam_test_setup_mech_rsa_pkcs(void **state) +{ + int rc = pam_test_setup_no_verification(state); + if (rc != 0) { + return rc; + } + return putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_mech_rsa_pkcs.conf")); +} + +static int pam_test_setup_mech_rsa_sha384_pkcs(void **state) +{ + int rc = pam_test_setup_no_verification(state); + if (rc != 0) { + return rc; + } + return putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_mech_rsa_sha384_pkcs.conf")); +} +#endif /* HAVE_TEST_CA */ + +static int pam_cached_test_setup(void **state) +{ + struct sss_test_conf_param dom_params[] = { + { "enumerate", "false" }, + { "cache_credentials", "true" }, + { "cached_auth_timeout", CACHED_AUTH_TIMEOUT_STR }, + { "local_auth_policy", "enable:smartcard" }, /* Needed to allow local sc auth */ + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param pam_params[] = { + { "p11_child_timeout", "30" }, + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "no_ocsp"}, + { NULL, NULL }, /* Sentinel */ + }; + + test_pam_setup(dom_params, pam_params, monitor_params, state); + + pam_test_setup_common(); + return 0; +} + +static int pam_test_teardown(void **state) +{ + int ret; + + ret = sysdb_delete_user(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, 0); + assert_int_equal(ret, EOK); + + ret = sysdb_delete_user(pam_test_ctx->tctx->dom, + pam_test_ctx->wrong_user_fqdn, 0); + assert_int_equal(ret, EOK); + + if (!no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + + talloc_free(pam_test_ctx); + return 0; +} + +typedef int (*cmd_cb_fn_t)(uint32_t, uint8_t *, size_t); + + +int __real_read_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **buf, ssize_t *len); + +void __real_sss_packet_get_body(struct sss_packet *packet, + uint8_t **body, size_t *blen); + +void __wrap_sss_packet_get_body(struct sss_packet *packet, + uint8_t **body, size_t *blen) +{ + enum sss_test_wrapper_call wtype = sss_mock_type(enum sss_test_wrapper_call); + size_t len; + + if (wtype == WRAP_CALL_REAL) { + return __real_sss_packet_get_body(packet, body, blen); + } + + *body = sss_mock_ptr_type(uint8_t *); + len = sss_mock_type(size_t); + if (len == 0) { + len = strlen((const char *) *body) + 1; + } + *blen = len; + return; +} + +void __real_sss_packet_get_body(struct sss_packet *packet, + uint8_t **body, size_t *blen); + +void __wrap_sss_cmd_done(struct cli_ctx *cctx, void *freectx) +{ + struct cli_protocol *prctx; + struct sss_packet *packet; + uint8_t *body; + size_t blen; + cmd_cb_fn_t check_cb; + + prctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); + packet = prctx->creq->out; + assert_non_null(packet); + + check_cb = sss_mock_ptr_type(cmd_cb_fn_t); + + __real_sss_packet_get_body(packet, &body, &blen); + + pam_test_ctx->tctx->error = check_cb(sss_packet_get_status(packet), + body, blen); + pam_test_ctx->tctx->done = true; +} + +enum sss_cli_command __wrap_sss_packet_get_cmd(struct sss_packet *packet) +{ + return sss_mock_type(enum sss_cli_command); +} + +int __wrap_sss_cmd_send_empty(struct cli_ctx *cctx, TALLOC_CTX *freectx) +{ + pam_test_ctx->tctx->done = true; + pam_test_ctx->tctx->error = ENOENT; + return EOK; +} + +static void set_cmd_cb(cmd_cb_fn_t fn) +{ + will_return(__wrap_sss_cmd_done, fn); +} + +int __wrap_pam_dp_send_req(struct pam_auth_req *preq, int timeout) +{ + pam_test_ctx->provider_contacted = true; + + /* Set expected status */ + preq->pd->pam_status = pam_test_ctx->exp_pam_status; + if (pam_test_ctx->pd->resp_list != NULL) { + preq->pd->resp_list = pam_test_ctx->pd->resp_list; + } + + preq->callback(preq); + + return EOK; +} + +#ifdef BUILD_PASSKEY +static void passkey_test_done(struct tevent_req *req) +{ + struct pam_test_ctx *ctx = + tevent_req_callback_data(req, struct pam_test_ctx); + + pam_passkey_auth_recv(req, &pam_test_ctx->child_status); + talloc_zfree(req); + + /* No actual fido2 device available, overwrite the child status to successful. + * as we are faking authentication */ + if (pam_test_ctx->child_status == 1) { + pam_test_ctx->child_status = 0; + } + + ctx->tctx->done = true; +} + +static void mock_input_pam_passkey(TALLOC_CTX *mem_ctx, + const char *name, + const char *pin, + const char *svc, + acct_cb_t acct_cb, + const char *passkey) +{ + size_t buf_size; + uint8_t *m_buf; + uint8_t *buf; + struct pam_items pi = { 0 }; + int ret; + char *s_name; + char *dom; + + if (name != NULL) { + pi.pam_user = name; + pi.pam_user_size = strlen(pi.pam_user) + 1; + } else { + pi.pam_user = ""; + pi.pam_user_size = 0; + } + + if (pin != NULL) { + pi.pam_authtok = discard_const(pin); + pi.pam_authtok_size = strlen(pi.pam_authtok) + 1; + pi.pam_authtok_type = SSS_AUTHTOK_TYPE_PASSKEY; + } + + if (svc == NULL) { + svc = "pam_test_service"; + } + pi.pam_service = svc; + pi.pam_service_size = strlen(pi.pam_service) + 1; + pi.pam_tty = "/dev/tty"; + pi.pam_tty_size = strlen(pi.pam_tty) + 1; + pi.pam_ruser = "remuser"; + pi.pam_ruser_size = strlen(pi.pam_ruser) + 1; + pi.pam_rhost = "remhost"; + pi.pam_rhost_size = strlen(pi.pam_rhost) + 1; + pi.requested_domains = ""; + pi.cli_pid = 12345; + + ret = pack_message_v3(&pi, &buf_size, &m_buf); + assert_int_equal(ret, 0); + + buf = talloc_memdup(mem_ctx, m_buf, buf_size); + free(m_buf); + assert_non_null(buf); + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, buf); + will_return(__wrap_sss_packet_get_body, buf_size); + + if (strrchr(name, '@') == NULL) { + mock_parse_inp(name, NULL, EOK); + } else { + ret = sss_parse_internal_fqname(mem_ctx, name, &s_name, &dom); + mock_parse_inp(s_name, dom, EOK); + } + + if (acct_cb != NULL) { + mock_account_recv(0, 0, NULL, acct_cb, discard_const(passkey)); + } +} + +static int test_pam_passkey_auth_check(uint32_t status, uint8_t *body, size_t blen) +{ + return EOK; +} + +static void set_passkey_auth_param(struct pam_ctx *pctx) +{ + pam_test_ctx->pctx->passkey_auth = true; +} +#endif + +static void mock_input_pam_ex(TALLOC_CTX *mem_ctx, + const char *name, + const char *pwd, + const char *fa2, + const char *svc, + bool contact_dp) +{ + size_t buf_size; + uint8_t *m_buf; + uint8_t *buf; + struct pam_items pi = { 0 }; + int ret; + size_t needed_size; + uint8_t *authtok; + char *s_name; + char *dom; + + if (name != NULL) { + pi.pam_user = name; + pi.pam_user_size = strlen(pi.pam_user) + 1; + } else { + pi.pam_user = ""; + pi.pam_user_size = 0; + } + + if (pwd != NULL) { + if (fa2 != NULL) { + ret = sss_auth_pack_2fa_blob(pwd, 0, fa2, 0, NULL, 0, &needed_size); + assert_int_equal(ret, EAGAIN); + + authtok = talloc_size(mem_ctx, needed_size); + assert_non_null(authtok); + + ret = sss_auth_pack_2fa_blob(pwd, 0, fa2, 0, authtok, + needed_size, &needed_size); + assert_int_equal(ret, EOK); + + pi.pam_authtok = (char *) authtok; + pi.pam_authtok_size = needed_size; + pi.pam_authtok_type = SSS_AUTHTOK_TYPE_2FA; + } else { + pi.pam_authtok = discard_const(pwd); + pi.pam_authtok_size = strlen(pi.pam_authtok) + 1; + pi.pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + } + } + + if (svc == NULL) { + svc = "pam_test_service"; + } + pi.pam_service = svc; + pi.pam_service_size = strlen(pi.pam_service) + 1; + pi.pam_tty = "/dev/tty"; + pi.pam_tty_size = strlen(pi.pam_tty) + 1; + pi.pam_ruser = "remuser"; + pi.pam_ruser_size = strlen(pi.pam_ruser) + 1; + pi.pam_rhost = "remhost"; + pi.pam_rhost_size = strlen(pi.pam_rhost) + 1; + pi.requested_domains = ""; + pi.cli_pid = 12345; + + ret = pack_message_v3(&pi, &buf_size, &m_buf); + assert_int_equal(ret, 0); + + buf = talloc_memdup(mem_ctx, m_buf, buf_size); + free(m_buf); + assert_non_null(buf); + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, buf); + will_return(__wrap_sss_packet_get_body, buf_size); + + if (strrchr(name, '@') == NULL) { + mock_parse_inp(name, NULL, EOK); + } else { + ret = sss_parse_internal_fqname(mem_ctx, name, &s_name, &dom); + mock_parse_inp(s_name, dom, EOK); + } + + if (contact_dp) { + mock_account_recv_simple(); + } +} + +static void mock_input_pam(TALLOC_CTX *mem_ctx, + const char *name, + const char *pwd, + const char *fa2) +{ + return mock_input_pam_ex(mem_ctx, name, pwd, fa2, NULL, false); +} + +static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name, + const char *pin, const char *token_name, + const char *module_name, const char *key_id, + const char *label, const char *service, + acct_cb_t acct_cb, const char *cert) +{ + size_t buf_size; + uint8_t *m_buf; + uint8_t *buf; + struct pam_items pi = { 0 }; + int ret; + size_t needed_size; + + if (name != NULL) { + pi.pam_user = name; + pi.pam_user_size = strlen(pi.pam_user) + 1; + } else { + pi.pam_user = ""; + pi.pam_user_size = 0; + } + + if (pin != NULL) { + ret = sss_auth_pack_sc_blob(pin, 0, token_name, 0, module_name, 0, + key_id, 0, label, 0, NULL, 0, &needed_size); + assert_int_equal(ret, EAGAIN); + + pi.pam_authtok = malloc(needed_size); + assert_non_null(pi.pam_authtok); + + ret = sss_auth_pack_sc_blob(pin, 0, token_name, 0, module_name, 0, + key_id, 0, label, 0, + (uint8_t *)pi.pam_authtok, needed_size, + &needed_size); + assert_int_equal(ret, EOK); + + pi.pam_authtok_type = SSS_AUTHTOK_TYPE_SC_PIN; + pi.pam_authtok_size = needed_size; + } + + pi.pam_service = service == NULL ? "login" : service; + pi.pam_service_size = strlen(pi.pam_service) + 1; + pi.pam_tty = "/dev/tty"; + pi.pam_tty_size = strlen(pi.pam_tty) + 1; + pi.pam_ruser = "remuser"; + pi.pam_ruser_size = strlen(pi.pam_ruser) + 1; + pi.pam_rhost = "remhost"; + pi.pam_rhost_size = strlen(pi.pam_rhost) + 1; + pi.requested_domains = ""; + pi.cli_pid = 12345; + + ret = pack_message_v3(&pi, &buf_size, &m_buf); + free(pi.pam_authtok); + assert_int_equal(ret, 0); + + buf = talloc_memdup(mem_ctx, m_buf, buf_size); + free(m_buf); + assert_non_null(buf); + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, buf); + will_return(__wrap_sss_packet_get_body, buf_size); + + if (acct_cb != NULL) { + mock_account_recv(0, 0, NULL, acct_cb, discard_const(cert)); + } + + if (name != NULL) { + mock_parse_inp(name, NULL, EOK); + } +} + +#ifdef BUILD_PASSKEY +static int test_pam_passkey_preauth_check(uint32_t status, uint8_t *body, size_t blen) +{ + size_t rp = 0; + uint32_t val; + + assert_int_equal(status, 0); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 2); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal((char *)(body + rp), TEST_DOM_NAME); + + return EOK; +} + +static int test_pam_passkey_found_preauth_check(uint32_t status, uint8_t *body, size_t blen) +{ + size_t rp = 0; + uint32_t val; + + assert_int_equal(status, 0); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 3); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal((char *)(body + rp), TEST_DOM_NAME); + + return EOK; +} +#endif /* BUILD_PASSKEY */ + +static int test_pam_simple_check(uint32_t status, uint8_t *body, size_t blen) +{ + size_t rp = 0; + uint32_t val; + + assert_int_equal(status, 0); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 1); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal((char *)(body + rp), TEST_DOM_NAME); + + return EOK; +} + +#define PKCS11_LOGIN_TOKEN_ENV_NAME "PKCS11_LOGIN_TOKEN_NAME" + +static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body, + size_t blen) +{ + size_t rp = 0; + uint32_t val; + + assert_int_equal(status, 0); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 3); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal((char *)(body + rp), TEST_DOM_NAME); + rp += val; + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_ENV_ITEM); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, (strlen(PKCS11_LOGIN_TOKEN_ENV_NAME "=") + + sizeof(TEST_TOKEN_NAME))); + assert_string_equal((char *)(body + rp), + PKCS11_LOGIN_TOKEN_ENV_NAME "=" TEST_TOKEN_NAME); + rp += val; + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_CERT_INFO); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME) + + sizeof(TEST_TOKEN_NAME) + + sizeof(TEST_MODULE_NAME) + + sizeof(TEST_KEY_ID) + + sizeof(TEST_LABEL) + + sizeof(TEST_PROMPT) + + sizeof("pamuser"))); + + assert_int_equal(*(body + rp + sizeof("pamuser@"TEST_DOM_NAME) - 1), 0); + assert_string_equal((char *)(body + rp), "pamuser@"TEST_DOM_NAME); + rp += sizeof("pamuser@"TEST_DOM_NAME); + + assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0); + assert_string_equal((char *)(body + rp), TEST_TOKEN_NAME); + rp += sizeof(TEST_TOKEN_NAME); + + assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0); + assert_string_equal((char *)(body + rp), TEST_MODULE_NAME); + rp += sizeof(TEST_MODULE_NAME); + + assert_int_equal(*(body + rp + sizeof(TEST_KEY_ID) - 1), 0); + assert_string_equal((char *)(body + rp), TEST_KEY_ID); + rp += sizeof(TEST_KEY_ID); + + assert_int_equal(*(body + rp + sizeof(TEST_LABEL) - 1), 0); + assert_string_equal((char *)(body + rp), TEST_LABEL); + rp += sizeof(TEST_LABEL); + + assert_int_equal(*(body + rp + sizeof(TEST_PROMPT) - 1), 0); + assert_string_equal((char *)(body + rp), TEST_PROMPT); + rp += sizeof(TEST_PROMPT); + + assert_int_equal(*(body + rp + sizeof("pamuser") - 1), 0); + assert_string_equal((char *)(body + rp), "pamuser"); + rp += sizeof("pamuser"); + + assert_int_equal(rp, blen); + return EOK; +} + +static void check_string_array(const char **strs, uint8_t *body, size_t *rp) +{ + size_t c; + + for (c = 0; strs[c] != NULL; c++) { + assert_int_equal(*(body + *rp + strlen(strs[c])), 0); + assert_string_equal((char *)(body + *rp), strs[c]); + *rp += strlen(strs[c]) + 1; + } +} + +static size_t check_string_array_len(const char **strs) +{ + size_t c; + size_t sum = 0; + + for (c = 0; strs[c] != NULL; c++) { + sum += strlen(strs[c]) + 1; + } + + return sum; +} + +static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen, + enum response_type type, const char *name, + const char *name2, const char *nss_name) +{ + size_t rp = 0; + uint32_t val; + bool test2_first = false; + + size_t check_len = 0; + char const *check_strings[] = { NULL, + TEST_TOKEN_NAME, + TEST_MODULE_NAME, + TEST_KEY_ID, + TEST_LABEL, + TEST_PROMPT, + NULL, + NULL }; + + size_t check2_len = 0; + char const *check2_strings[] = { NULL, + TEST_TOKEN_NAME, + TEST_MODULE_NAME, + TEST2_KEY_ID, + TEST2_LABEL, + TEST2_PROMPT, + NULL, + NULL }; + + assert_int_equal(status, 0); + + check_strings[0] = name; + check_strings[6] = nss_name; + check_len = check_string_array_len(check_strings); + check2_strings[0] = name; + check2_strings[6] = nss_name; + check2_len = check_string_array_len(check2_strings); + + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + if (name == NULL || *name == '\0') { + assert_int_equal(val, 1); + } else { + if (name2 == NULL || *name2 == '\0') { + assert_int_equal(val, 2); + } else { + assert_int_equal(val, 3); + } + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal((char *)(body + rp), TEST_DOM_NAME); + rp += val; + } + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, type); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + + /* look ahead to check if the certificate #2 comes first */ + if (name2 != NULL && *name2 != '\0' + && val == check2_len + && strncmp((char *) body + rp + strlen(name) + 1 + + sizeof(TEST_TOKEN_NAME) + + sizeof(TEST_MODULE_NAME), + TEST2_KEY_ID, + sizeof(TEST2_KEY_ID)) == 0 ) { + test2_first = true; + + assert_int_equal(val, check2_len); + + check_string_array(check2_strings, body, &rp); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, type); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + } + + assert_int_equal(val, check_len); + + check_string_array(check_strings, body, &rp); + + if (name2 != NULL && *name2 != '\0' && !test2_first) { + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, type); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, check2_len); + + check_string_array(check2_strings, body, &rp); + } + + assert_int_equal(rp, blen); + + return EOK; +} + +static int test_pam_cert2_token2_check_ex(uint32_t status, uint8_t *body, + size_t blen, enum response_type type, + const char *name, + const char *nss_name) +{ + size_t rp = 0; + uint32_t val; + size_t check2_len = 0; + char const *check2_strings[] = { NULL, + TEST_TOKEN2_NAME, + TEST_MODULE_NAME, + TEST2_KEY_ID, + TEST2_LABEL, + TEST2_PROMPT, + NULL, + NULL }; + + assert_int_equal(status, 0); + + check2_strings[0] = name; + check2_strings[6] = nss_name; + check2_len = check_string_array_len(check2_strings); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 2); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal((char *)(body + rp), TEST_DOM_NAME); + rp += val; + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, type); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, check2_len); + + check_string_array(check2_strings, body, &rp); + + assert_int_equal(rp, blen); + + return EOK; +} + +static int test_pam_cert_X_token_X_check_ex(uint32_t status, uint8_t *body, + size_t blen, enum response_type type, + const char *name, + const char *nss_name, + char const *check_strings[]) +{ + size_t rp = 0; + uint32_t val; + size_t check_len = 0; + + assert_int_equal(status, 0); + + check_strings[0] = name; + check_strings[6] = nss_name; + check_len = check_string_array_len(check_strings); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 2); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal((char *)(body + rp), TEST_DOM_NAME); + rp += val; + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, type); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, check_len); + + check_string_array(check_strings, body, &rp); + + assert_int_equal(rp, blen); + + return EOK; +} + +static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen) +{ + return test_pam_cert_check_ex(status, body, blen, + SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME, + NULL, "pamuser"); +} + +static int test_pam_cert2_check(uint32_t status, uint8_t *body, size_t blen) +{ + return test_pam_cert2_token2_check_ex(status, body, blen, SSS_PAM_CERT_INFO, + "pamuser@"TEST_DOM_NAME, "pamuser"); +} + +static int test_pam_cert5_check(uint32_t status, uint8_t *body, size_t blen) +{ + char const *check5_strings[] = { NULL, + TEST_TOKEN_NAME, + TEST_MODULE_NAME, + TEST5_KEY_ID, + TEST5_LABEL, + TEST5_PROMPT, + NULL, + NULL }; + return test_pam_cert_X_token_X_check_ex(status, body, blen, + SSS_PAM_CERT_INFO, + "pamuser@"TEST_DOM_NAME, "pamuser", + check5_strings); +} + +static int test_pam_cert_check_auth_success(uint32_t status, uint8_t *body, + size_t blen) +{ + assert_int_equal(pam_test_ctx->exp_pam_status, PAM_BAD_ITEM); + pam_test_ctx->exp_pam_status = PAM_SUCCESS; + return test_pam_cert_check_ex(status, body, blen, + SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME, + NULL, "pamuser"); +} + +static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body, + size_t blen) +{ + return test_pam_cert_check_ex(status, body, blen, + SSS_PAM_CERT_INFO_WITH_HINT, + "pamuser@"TEST_DOM_NAME, NULL, + "pamuser"); +} + +static int test_pam_cert_check_with_hint_no_user(uint32_t status, uint8_t *body, + size_t blen) +{ + return test_pam_cert_check_ex(status, body, blen, + SSS_PAM_CERT_INFO_WITH_HINT, "", NULL, ""); +} + +static int test_pam_cert_check_2certs(uint32_t status, uint8_t *body, + size_t blen) +{ + return test_pam_cert_check_ex(status, body, blen, + SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME, + "pamuser@"TEST_DOM_NAME, + "pamuser"); +} + + + +static int test_pam_offline_chauthtok_check(uint32_t status, + uint8_t *body, size_t blen) +{ + size_t rp = 0; + uint32_t val; + + pam_test_ctx->exp_pam_status = PAM_AUTHTOK_ERR; + + assert_int_equal(status, 0); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 2); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal((char *)(body + rp), TEST_DOM_NAME); + rp += val; + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_USER_INFO); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 4); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_USER_INFO_OFFLINE_CHPASS); + + return EOK; +} + + +static int test_pam_failed_offline_auth_check(uint32_t status, uint8_t *body, + size_t blen) +{ + pam_test_ctx->exp_pam_status = PAM_PERM_DENIED; + return test_pam_simple_check(status, body, blen); +} + +static int test_pam_successful_offline_auth_check(uint32_t status, + uint8_t *body, size_t blen) +{ + pam_test_ctx->exp_pam_status = PAM_SUCCESS; + return test_pam_simple_check(status, body, blen); +} + +static int test_pam_successful_cached_auth_check(uint32_t status, + uint8_t *body, size_t blen) +{ + pam_test_ctx->exp_pam_status = PAM_SUCCESS; + return test_pam_simple_check(status, body, blen); +} + +static int test_pam_wrong_pw_offline_auth_check(uint32_t status, + uint8_t *body, size_t blen) +{ + pam_test_ctx->exp_pam_status = PAM_AUTH_ERR; + return test_pam_simple_check(status, body, blen); +} + +static int test_pam_simple_check_success(uint32_t status, + uint8_t *body, size_t blen) +{ + pam_test_ctx->exp_pam_status = PAM_SUCCESS; + return test_pam_simple_check(status, body, blen); +} + +static int test_pam_creds_insufficient_check(uint32_t status, + uint8_t *body, size_t blen) +{ + size_t rp = 0; + uint32_t val; + + assert_int_equal(status, 0); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, PAM_CRED_INSUFFICIENT); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 0); + + return EOK; +} + +static int test_pam_auth_err_check(uint32_t status, uint8_t *body, size_t blen) +{ + /* PAM_AUTH_ERR is returned for different types of error, we use different + * names for the check functions to make the purpose more clear. */ + return test_pam_wrong_pw_offline_auth_check(status, body, blen); +} + +static int test_pam_user_unknown_check(uint32_t status, + uint8_t *body, size_t blen) +{ + size_t rp = 0; + uint32_t val; + + assert_int_equal(status, 0); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, PAM_USER_UNKNOWN); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 0); + + return EOK; +} + +void test_pam_authenticate(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_setcreds(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_SETCRED); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_SETCRED, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_acct_mgmt(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_ACCT_MGMT); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_ACCT_MGMT, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_open_session(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_OPEN_SESSION); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* make sure pam_status is not touched by setting it to a value which is + * not used by SSSD. */ + pam_test_ctx->exp_pam_status = _PAM_RETURN_VALUES; + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_OPEN_SESSION, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_close_session(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CLOSE_SESSION); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CLOSE_SESSION, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_chauthtok(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CHAUTHTOK); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CHAUTHTOK, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_chauthtok_prelim(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CHAUTHTOK_PRELIM); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CHAUTHTOK_PRELIM, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Cached on-line authentication */ + +static void common_test_pam_cached_auth(const char *pwd) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", pwd, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_SUCCESS; + set_cmd_cb(test_pam_successful_cached_auth_check); + + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cached_auth_success(void **state) +{ + int ret; + + common_test_pam_cached_auth("12345"); + + /* Back end should be contacted */ + assert_true(pam_test_ctx->provider_contacted); + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + + /* Reset before next call */ + pam_test_ctx->provider_contacted = false; + + pam_test_ctx->tctx->done = false; + + common_test_pam_cached_auth("12345"); + + /* Back end should not be contacted */ + assert_false(pam_test_ctx->provider_contacted); +} + +void test_pam_cached_auth_wrong_pw(void **state) +{ + int ret; + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + + ret = pam_set_last_online_auth_with_curr_token(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + time(NULL)); + assert_int_equal(ret, EOK); + + common_test_pam_cached_auth("11111"); + + /* Back end should be contacted */ + assert_true(pam_test_ctx->provider_contacted); +} + +/* test cached_auth_timeout option */ +void test_pam_cached_auth_opt_timeout(void **state) +{ + int ret; + uint64_t last_online; + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + + last_online = time(NULL) - CACHED_AUTH_TIMEOUT - 1; + ret = pam_set_last_online_auth_with_curr_token(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + last_online); + assert_int_equal(ret, EOK); + + common_test_pam_cached_auth("12345"); + + /* Back end should be contacted */ + assert_true(pam_test_ctx->provider_contacted); +} + +/* too long since last on-line authentication */ +void test_pam_cached_auth_timeout(void **state) +{ + int ret; + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + + ret = pam_set_last_online_auth_with_curr_token(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + 0); + assert_int_equal(ret, EOK); + + common_test_pam_cached_auth("12345"); + + /* Back end should be contacted */ + assert_true(pam_test_ctx->provider_contacted); +} + +void test_pam_cached_auth_success_combined_pw_with_cached_2fa(void **state) +{ + int ret; + + common_test_pam_cached_auth("12345678"); + + assert_true(pam_test_ctx->provider_contacted); + + ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345678", SSS_AUTHTOK_TYPE_2FA, 5); + assert_int_equal(ret, EOK); + + /* Reset before next call */ + pam_test_ctx->provider_contacted = false; + + pam_test_ctx->tctx->done = false; + + common_test_pam_cached_auth("12345678"); + + assert_false(pam_test_ctx->provider_contacted); +} + +void test_pam_cached_auth_failed_combined_pw_with_cached_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345678", SSS_AUTHTOK_TYPE_2FA, 5); + assert_int_equal(ret, EOK); + ret = pam_set_last_online_auth_with_curr_token(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + time(NULL)); + assert_int_equal(ret, EOK); + + common_test_pam_cached_auth("1111abcde"); + + assert_true(pam_test_ctx->provider_contacted); +} + +/* Off-line authentication */ + +void test_pam_offline_auth_no_hash(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", "12345", NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_failed_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_success(void **state) +{ + int ret; + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "12345", NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_successful_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_wrong_pw(void **state) +{ + int ret; + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "11111", NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_wrong_pw_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_success_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "12345", "abcde"); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_successful_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_failed_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "11111", "abcde"); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_wrong_pw_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_success_2fa_with_cached_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345", + SSS_AUTHTOK_TYPE_2FA, 5); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "12345", "abcde"); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_successful_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_failed_2fa_with_cached_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345", + SSS_AUTHTOK_TYPE_2FA, 5); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "11111", "abcde"); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_wrong_pw_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_success_pw_with_cached_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345", + SSS_AUTHTOK_TYPE_2FA, 5); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "12345", NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_successful_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_failed_pw_with_cached_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345", + SSS_AUTHTOK_TYPE_2FA, 5); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "11111", NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_wrong_pw_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_success_combined_pw_with_cached_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345678", SSS_AUTHTOK_TYPE_2FA, 5); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "12345678abcde", NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_successful_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_failed_combined_pw_with_cached_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345678", SSS_AUTHTOK_TYPE_2FA, 5); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "11111111abcde", NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_wrong_pw_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_auth_failed_wrong_2fa_size_with_cached_2fa(void **state) +{ + int ret; + + ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345678", SSS_AUTHTOK_TYPE_2FA, 5); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", "12345678abcd", NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_wrong_pw_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_chauthtok_prelim(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CHAUTHTOK_PRELIM); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_offline_chauthtok_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CHAUTHTOK_PRELIM, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_offline_chauthtok(void **state) +{ + int ret; + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CHAUTHTOK); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; + + set_cmd_cb(test_pam_offline_chauthtok_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CHAUTHTOK, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth_no_logon_name(void **state) +{ + int ret; + + mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_creds_insufficient_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_auth_no_upn_logon_name(void **state) +{ + int ret; + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + + mock_input_pam_ex(pam_test_ctx, "upn@"TEST_DOM_NAME, "12345", NULL, NULL, + false); + mock_account_recv_simple(); + mock_parse_inp("upn@"TEST_DOM_NAME, NULL, EOK); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN; + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_auth_upn_logon_name(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + + ret = sysdb_cache_password(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + "12345"); + assert_int_equal(ret, EOK); + attrs = sysdb_new_attrs(pam_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, "upn@"TEST_DOM_NAME); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + assert_int_equal(ret, EOK); + + mock_input_pam_ex(pam_test_ctx, "upn@"TEST_DOM_NAME, "12345", NULL, NULL, + true); + + mock_parse_inp("pamuser", NULL, EOK); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_successful_offline_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + + + +static void set_cert_auth_param(struct pam_ctx *pctx, const char *dbpath) +{ + pam_test_ctx->pctx->cert_auth = true; + pam_test_ctx->pctx->ca_db = discard_const(dbpath); +} + +void test_pam_preauth_cert_nocert(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + unsetenv("SOFTHSM2_CONF"); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int test_lookup_by_cert_cb(void *pvt) +{ + int ret; + struct sysdb_attrs *attrs; + unsigned char *der = NULL; + size_t der_size; + + if (pvt != NULL) { + + attrs = sysdb_new_attrs(pam_test_ctx); + assert_non_null(attrs); + + der = sss_base64_decode(pam_test_ctx, pvt, &der_size); + assert_non_null(der); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + assert_int_equal(ret, EOK); + } + + return EOK; +} + +static int test_lookup_by_cert_cb_2nd_cert_same_user(void *pvt) +{ + int ret; + struct sysdb_attrs *attrs; + unsigned char *der = NULL; + size_t der_size; + + test_lookup_by_cert_cb(pvt); + + attrs = sysdb_new_attrs(pam_test_ctx); + assert_non_null(attrs); + + der = sss_base64_decode(pam_test_ctx, SSSD_TEST_CERT_0002, &der_size); + assert_non_null(der); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + assert_int_equal(ret, EOK); + + return EOK; +} + +static int test_lookup_by_cert_double_cb(void *pvt) +{ + int ret; + struct sysdb_attrs *attrs; + unsigned char *der = NULL; + size_t der_size; + + if (pvt != NULL) { + + ret = test_lookup_by_cert_cb(pvt); + assert_int_equal(ret, EOK); + + attrs = sysdb_new_attrs(pam_test_ctx); + assert_non_null(attrs); + + der = sss_base64_decode(pam_test_ctx, pvt, &der_size); + assert_non_null(der); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, + pam_test_ctx->wrong_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + assert_int_equal(ret, EOK); + } + + return EOK; +} + +static int test_lookup_by_cert_wrong_user_cb(void *pvt) +{ + int ret; + struct sysdb_attrs *attrs; + unsigned char *der = NULL; + size_t der_size; + + if (pvt != NULL) { + attrs = sysdb_new_attrs(pam_test_ctx); + assert_non_null(attrs); + + der = sss_base64_decode(pam_test_ctx, pvt, &der_size); + assert_non_null(der); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, + pam_test_ctx->wrong_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + assert_int_equal(ret, EOK); + } + + return EOK; +} + +static void pam_preauth(struct sss_test_conf_param monitor_params[], + acct_cb_t acct_cb, const char *cert, + cmd_cb_fn_t fn) +{ + int ret; + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, acct_cb, cert); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(fn); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + + +void test_pam_preauth_cert_nomatch(void **state) +{ + int ret; + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_one.conf")); + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth_cert_match(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* Test if PKCS11_LOGIN_TOKEN_NAME is added for the gdm-smartcard service */ +void test_pam_preauth_cert_match_gdm_smartcard(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + "gdm-smartcard", test_lookup_by_cert_cb, + SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check_gdm_smartcard); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth_cert_match_wrong_user(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_wrong_user_cb, + SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + + +void test_pam_preauth_cert_no_logon_name(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + /* If no logon name is given the user is looked by certificate first. + * Since there is a matching user the upcoming lookup by name will find + * the user entry. But since we force the lookup by name to go to the + * backend to make sure the group-membership data is up to date the + * backend response has to be mocked twice. + * Additionally sss_parse_inp_recv() must be mocked because the cache + * request will be done with the username found by the certificate + * lookup. */ + mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001); + mock_account_recv_simple(); + mock_parse_inp("pamuser", NULL, EOK); + mock_parse_inp("pamuser", NULL, EOK); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth_cert_no_logon_name_with_hint(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + pam_test_ctx->rctx->domains->user_name_hint = true; + + /* If no logon name is given the user is looked by certificate first. + * Since user name hint is enabled we do not have to search the user + * during pre-auth and there is no need for an extra mocked response as in + * test_pam_preauth_cert_no_logon_name. */ + mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check_with_hint); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth_cert_no_logon_name_double_cert(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_creds_insufficient_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth_cert_no_logon_name_double_cert_with_hint(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + pam_test_ctx->rctx->domains->user_name_hint = true; + + mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check_with_hint_no_user); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth_no_cert_no_logon_name(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, "/no/path"); + + mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_user_unknown_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth_cert_no_logon_name_no_match(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_user_unknown_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_auth(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token", + TEST_MODULE_NAME, + "C554C9F82C2A9D58B70921C143304153A8A42F17", + "SSSD test cert 0001", NULL, + test_lookup_by_cert_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_pss_cert_auth(void **state) +{ + int ret; + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_pss_one.conf")); + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token", + TEST_MODULE_NAME, + "C554C9F82C2A9D58B70921C143304153A8A42F17", + "SSSD test cert 0007", NULL, + test_lookup_by_cert_cb, SSSD_TEST_CERT_0007); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_ecc_cert_auth(void **state) +{ + int ret; + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_ECC_CA/softhsm2_ecc_one.conf")); + + set_cert_auth_param(pam_test_ctx->pctx, ECC_CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", + "SSSD Test ECC Token", + TEST_MODULE_NAME, + "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB", + "SSSD test ECC cert 0001", NULL, + test_lookup_by_cert_cb, SSSD_TEST_ECC_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_intermediate_ca_cert_auth_with_full_certs(void **state) +{ + int ret; + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf")); + + set_cert_auth_param(pam_test_ctx->pctx, INTERMEDIATE_FULL_CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", + "SSSD Test intermediate CA Token", + TEST_MODULE_NAME, + "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB", + "SSSD test intermediate cert 0001", NULL, + test_lookup_by_cert_cb, + SSSD_TEST_INTERMEDIATE_CA_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_intermediate_ca_cert_auth_fails_with_incomplete_db(void **state) +{ + int ret; + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf")); + + set_cert_auth_param(pam_test_ctx->pctx, INTERMEDIATE_CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", + "SSSD Test intermediate CA Token", + TEST_MODULE_NAME, + "1234567890", + NULL, NULL, NULL, + SSSD_TEST_INTERMEDIATE_CA_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_auth_err_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_intermediate_ca_cert_auth_with_partial_chain(void **state) +{ + int ret; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "partial_chain" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf")); + + set_cert_auth_param(pam_test_ctx->pctx, INTERMEDIATE_CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", + "SSSD Test intermediate CA Token", + TEST_MODULE_NAME, + "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB", + "SSSD test intermediate cert 0001", NULL, + test_lookup_by_cert_cb, + SSSD_TEST_INTERMEDIATE_CA_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_intermediate_ca_cert_auth_with_full_certs_and_partial_chain(void **state) +{ + int ret; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "partial_chain" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf")); + + set_cert_auth_param(pam_test_ctx->pctx, INTERMEDIATE_FULL_CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", + "SSSD Test intermediate CA Token", + TEST_MODULE_NAME, + "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB", + "SSSD test intermediate cert 0001", NULL, + test_lookup_by_cert_cb, + SSSD_TEST_INTERMEDIATE_CA_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_intermediate_ca_cert_auth_fails_with_root_and_partial_chain(void **state) +{ + int ret; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "partial_chain" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf")); + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", + "SSSD Test intermediate CA Token", + TEST_MODULE_NAME, + "9876543210", + NULL, NULL, NULL, + SSSD_TEST_INTERMEDIATE_CA_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_auth_err_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_intermediate_ca_cert_auth_with_partial_chain_pam_option(void **state) +{ + int ret; + + struct sss_test_conf_param pam_params[] = { + { "pam_cert_verification", "no_ocsp, partial_chain" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf")); + + set_cert_auth_param(pam_test_ctx->pctx, INTERMEDIATE_CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", + "SSSD Test intermediate CA Token", + TEST_MODULE_NAME, + "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB", + "SSSD test intermediate cert 0001", NULL, + test_lookup_by_cert_cb, + SSSD_TEST_INTERMEDIATE_CA_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_auth_no_logon_name(void **state) +{ + int ret; + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_one.conf")); + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token", + TEST_MODULE_NAME, + "C554C9F82C2A9D58B70921C143304153A8A42F17", + "SSSD test cert 0001", NULL, + test_lookup_by_cert_cb, SSSD_TEST_CERT_0001); + + mock_account_recv_simple(); + mock_parse_inp("pamuser", NULL, EOK); + mock_parse_inp("pamuser", NULL, EOK); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_cert_check_auth_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_auth_no_logon_name_no_key_id(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + /* Here the last option must be set to true because the backend is only + * connected once. During authentication the backend is connected first to + * see if it can handle Smartcard authentication, but before that the user + * is looked up. Since the first mocked reply already adds the certificate + * to the user entry the lookup by certificate will already find the user + * in the cache and no second request to the backend is needed. */ + mock_input_pam_cert(pam_test_ctx, NULL, "123456", "SSSD Test Token", + TEST_MODULE_NAME, NULL, NULL, NULL, + NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_creds_insufficient_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_auth_double_cert(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token", + TEST_MODULE_NAME, + "C554C9F82C2A9D58B70921C143304153A8A42F17", + "SSSD test cert 0001", NULL, + test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_preauth_2certs_one_mapping(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_two.conf")); + + ret = test_lookup_by_cert_cb(discard_const(SSSD_TEST_CERT_0001)); + assert_int_equal(ret, EOK); + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_preauth_2certs_two_mappings(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_two.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb_2nd_cert_same_user, + SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check_2certs); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_auth_2certs_one_mapping(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_two.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token", + TEST_MODULE_NAME, + "C554C9F82C2A9D58B70921C143304153A8A42F17", + "SSSD test cert 0001", NULL, + test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* The following three tests cover a use case where multiple certificates are + * using the same key-pair. According to PKCS#11 specs "The CKA_ID field is + * intended to distinguish among multiple keys. In the case of public and + * private keys, this field assists in handling multiple keys held by the same + * subject; the key identifier for a public key and its corresponding private + * key should be the same. The key identifier should also be the same as for + * the corresponding certificate, if one exists. Cryptoki does not enforce + * these associations, however." As a result certificates sharing the same + * key-pair will have the same id on the Smartcard. This means a second + * parameter is needed to distinguish them. We use the label here. + * + * The first test makes sure authentication fails is the label is missing, the + * second and third test make sure that each certificate can be selected with + * the proper label. */ +void test_pam_cert_auth_2certs_same_id_no_label(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2certs_same_id.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token", + TEST_MODULE_NAME, + "11111111", + NULL, NULL, + NULL, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_auth_err_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_auth_2certs_same_id_with_label_1(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2certs_same_id.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token", + TEST_MODULE_NAME, + "11111111", + "SSSD test cert 0001", NULL, + test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_auth_2certs_same_id_with_label_6(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2certs_same_id.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", "123456", "SSSD Test Token", + TEST_MODULE_NAME, + "11111111", + "SSSD test cert 0006", NULL, + test_lookup_by_cert_double_cb, SSSD_TEST_CERT_0006); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Assume backend cannot handle Smartcard credentials */ + pam_test_ctx->exp_pam_status = PAM_BAD_ITEM; + + set_cmd_cb(test_pam_simple_check_success); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_preauth_uri_token1(void **state) +{ + int ret; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_P11_URI, "pkcs11:token=SSSD%20Test%20Token" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2tokens.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_preauth_uri_token2(void **state) +{ + int ret; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_P11_URI, "pkcs11:token=SSSD%20Test%20Token%20Number%202" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2tokens.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0002); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert2_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* with an expired CRL file no certificate should be returned */ +void test_pam_preauth_expired_crl_file(void **state) +{ + int ret; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_expired_crl.pem" }, + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_P11_URI, NULL }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_preauth_expired_crl_file_soft(void **state) +{ + int ret; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "soft_crl,crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_expired_crl.pem" }, + { NULL, NULL }, /* Sentinel */ + }; + + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_one.conf")); + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* with enabled OCSP no certificate should be returned becasuse there is not + * OCSP responder available. */ +void test_pam_preauth_ocsp(void **state) +{ + int ret; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", NULL }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_ocsp.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* If no_ocsp is set, there should be a certificate returned even if a OCSP + * URI is set in the certificate. */ +void test_pam_preauth_ocsp_no_ocsp(void **state) +{ + int ret; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "no_ocsp" }, + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_P11_URI, "pkcs11:manufacturer=SoftHSM%20project" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_ocsp.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0005); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert5_check); + + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* If soft_ocsp is set, there should be a certificate returned even if a OCSP + * URI is set in the certificate. */ +void test_pam_preauth_ocsp_soft_ocsp(void **state) +{ + int ret; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "soft_ocsp" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_ocsp.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0005); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert5_check); + + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +/* With one valid and one invalid CRL files, + * a certificate should be returned */ +void test_pam_preauth_crl_valid_crl_invalid_files(void **state) +{ + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", + "crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_crl.pem," + "crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_expired_crl.pem" }, + { NULL, NULL }, /* Sentinel */ + }; + + pam_preauth(monitor_params, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, + test_pam_cert_check); +} + +/* With one CRL from another CA and the other one from the same CA but invalid, + * no certificate should be returned */ +void test_pam_preauth_crl_another_ca_crl_invalid_files(void **state) +{ + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", + "crl_file=" ABS_BUILD_DIR "/src/tests/test_ECC_CA/SSSD_test_ECC_crl.pem," + "crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_expired_crl.pem" }, + { NULL, NULL }, /* Sentinel */ + }; + + pam_preauth(monitor_params, NULL, NULL, test_pam_simple_check); +} + +/* With one CRL from the same CA but invalid and the other one from another CA, + * no certificate should be returned */ +void test_pam_preauth_crl_invalid_crl_another_ca_files(void **state) +{ + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", + "crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_expired_crl.pem," + "crl_file=" ABS_BUILD_DIR "/src/tests/test_ECC_CA/SSSD_test_ECC_crl.pem" }, + { NULL, NULL }, /* Sentinel */ + }; + + pam_preauth(monitor_params, NULL, NULL, test_pam_simple_check); +} + +/* With two valid CRL files, + * the first one from another CA and the second from the same CA, + * a certificate should be returned */ +void test_pam_preauth_first_crl_another_ca_files(void **state) +{ + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", + "crl_file=" ABS_BUILD_DIR "/src/tests/test_ECC_CA/SSSD_test_ECC_crl.pem," + "crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_crl.pem" }, + { NULL, NULL }, /* Sentinel */ + }; + + pam_preauth(monitor_params, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, + test_pam_cert_check); +} + +/* With two valid CRL files, + * the first one from the same CA and the second from another CA, + * a certificate should be returned */ +void test_pam_preauth_last_crl_another_ca_files(void **state) +{ + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", + "crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_crl.pem," + "crl_file=" ABS_BUILD_DIR "/src/tests/test_ECC_CA/SSSD_test_ECC_crl.pem" }, + { NULL, NULL }, /* Sentinel */ + }; + + pam_preauth(monitor_params, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, + test_pam_cert_check); +} + +void test_filter_response(void **state) +{ + int ret; + struct pam_data *pd; + uint8_t offline_auth_data[(sizeof(uint32_t) + sizeof(int64_t))]; + uint32_t info_type; + char *env; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_VERBOSITY, "1" }, + { CONFDB_PAM_RESPONSE_FILTER, NULL }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + pd = talloc_zero(pam_test_ctx, struct pam_data); + assert_non_null(pd); + + pd->service = discard_const("MyService"); + + env = talloc_asprintf(pd, "%s=%s", "MyEnv", "abcdef"); + assert_non_null(env); + + ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, + strlen(env) + 1, (uint8_t *) env); + assert_int_equal(ret, EOK); + + info_type = SSS_PAM_USER_INFO_OFFLINE_AUTH; + memset(offline_auth_data, 0, sizeof(offline_auth_data)); + memcpy(offline_auth_data, &info_type, sizeof(uint32_t)); + ret = pam_add_response(pd, SSS_PAM_USER_INFO, + sizeof(offline_auth_data), offline_auth_data); + assert_int_equal(ret, EOK); + + /* pd->resp_list points to the SSS_PAM_USER_INFO and pd->resp_list->next + * to the SSS_PAM_ENV_ITEM message. */ + + pam_test_ctx->pctx->rctx = pam_test_ctx->rctx; + + /* Test CONFDB_PAM_VERBOSITY option */ + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_false(pd->resp_list->next->do_not_send_to_client); + + /* SSS_PAM_USER_INFO_OFFLINE_AUTH message will only be shown with + * pam_verbosity 2 or above if cache password never expires. */ + pam_params[0].value = "2"; + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_false(pd->resp_list->do_not_send_to_client); + assert_false(pd->resp_list->next->do_not_send_to_client); + + pam_params[0].value = "0"; + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_false(pd->resp_list->next->do_not_send_to_client); + + /* Test CONFDB_PAM_RESPONSE_FILTER option */ + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "NoSuchOption"; + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_false(pd->resp_list->next->do_not_send_to_client); + + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV"; /* filter all environment variables */ + /* for all services */ + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_true(pd->resp_list->next->do_not_send_to_client); + + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV:"; /* filter all environment variables */ + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_true(pd->resp_list->next->do_not_send_to_client); + + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV::"; /* filter all environment variables */ + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_true(pd->resp_list->next->do_not_send_to_client); + + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV:abc:"; /* variable name does not match */ + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_false(pd->resp_list->next->do_not_send_to_client); + + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV:abc:MyService"; /* variable name does not match */ + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_false(pd->resp_list->next->do_not_send_to_client); + + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV::abc"; /* service name does not match */ + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_false(pd->resp_list->next->do_not_send_to_client); + + /* service name does not match */ + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV:MyEnv:abc"; + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_false(pd->resp_list->next->do_not_send_to_client); + + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV:MyEnv"; /* match */ + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_true(pd->resp_list->next->do_not_send_to_client); + + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV:MyEnv:"; /* match */ + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_true(pd->resp_list->next->do_not_send_to_client); + + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV:MyEnv:MyService"; /* match */ + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_true(pd->resp_list->next->do_not_send_to_client); + + /* multiple rules with a match */ + talloc_zfree(pam_test_ctx->pctx->pam_filter_opts); + pam_params[1].value = "ENV:abc:def, " + "ENV:MyEnv:MyService, " + "ENV:stu:xyz"; + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_true(pd->resp_list->next->do_not_send_to_client); + + talloc_free(pd); +} + +#define ENV_1 "MyEnv=abcdef" +#define ENV_2 "KRB5CCNAME=abc" +void test_filter_response_defaults(void **state) +{ + int ret; + struct pam_data *pd; + uint8_t offline_auth_data[(sizeof(uint32_t) + sizeof(int64_t))]; + uint32_t info_type; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_VERBOSITY, "1" }, + { CONFDB_PAM_RESPONSE_FILTER, NULL }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + pd = talloc_zero(pam_test_ctx, struct pam_data); + assert_non_null(pd); + + /* Currently only KRB5CCNAME is filtered for sudo and sudo-i, so all other + * environment variables and all other services should not be affected */ + pd->service = discard_const("MyService"); + + ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, + strlen(ENV_1) + 1, (const uint8_t *) ENV_1); + assert_int_equal(ret, EOK); + + ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, + strlen(ENV_2) + 1, (const uint8_t *) ENV_2); + assert_int_equal(ret, EOK); + + info_type = SSS_PAM_USER_INFO_OFFLINE_AUTH; + memset(offline_auth_data, 0, sizeof(offline_auth_data)); + memcpy(offline_auth_data, &info_type, sizeof(uint32_t)); + ret = pam_add_response(pd, SSS_PAM_USER_INFO, + sizeof(offline_auth_data), offline_auth_data); + assert_int_equal(ret, EOK); + + /* pd->resp_list points to the SSS_PAM_USER_INFO and pd->resp_list->next + * to the SSS_PAM_ENV_ITEM message with KRB5CCNAME and + * pd->resp_list->next->next to the SSS_PAM_ENV_ITEM message with MyEnv. */ + + pam_test_ctx->pctx->rctx = pam_test_ctx->rctx; + + + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_false(pd->resp_list->next->do_not_send_to_client); + assert_false(pd->resp_list->next->next->do_not_send_to_client); + + pd->service = discard_const("sudo"); + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_true(pd->resp_list->next->do_not_send_to_client); + assert_false(pd->resp_list->next->next->do_not_send_to_client); + + pd->service = discard_const("sudo-i"); + ret = filter_responses(pam_test_ctx->pctx, pd->resp_list, pd); + assert_int_equal(ret, EOK); + assert_true(pd->resp_list->do_not_send_to_client); + assert_true(pd->resp_list->next->do_not_send_to_client); + assert_false(pd->resp_list->next->next->do_not_send_to_client); + + talloc_free(pd); +} + +static int pam_test_setup_appsvc_posix_dom(void **state) +{ + int ret; + + ret = pam_test_setup(state); + if (ret != EOK) { + return ret; + } + + /* This config option is only read on startup, which is not executed + * in test, so we can't just pass in a param + */ + pam_test_ctx->pctx->app_services[0] = discard_const("app_svc"); + return 0; +} + +void test_appsvc_posix_dom(void **state) +{ + int ret; + + /* The domain is POSIX, the request will skip over it */ + mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "app_svc", false); + mock_parse_inp("pamuser", NULL, EOK); + pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN; + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_user_unknown_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_not_appsvc_posix_dom(void **state) +{ + int ret; + + /* A different service than the app one can authenticate against a POSIX domain */ + mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "not_app_svc", false); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int pam_test_setup_appsvc_app_dom(void **state) +{ + struct sss_test_conf_param dom_params[] = { + { "domain_type", "application" }, + { NULL, NULL }, /* Sentinel */ + }; + struct sss_test_conf_param pam_params[] = { + { NULL, NULL }, /* Sentinel */ + }; + struct sss_test_conf_param monitor_params[] = { + { NULL, NULL }, /* Sentinel */ + }; + + + test_pam_setup(dom_params, pam_params, monitor_params, state); + pam_test_setup_common(); + + /* This config option is only read on startup, which is not executed + * in test, so we can't just pass in a param + */ + pam_test_ctx->pctx->app_services[0] = discard_const("app_svc"); + return 0; +} + +void test_appsvc_app_dom(void **state) +{ + int ret; + + /* The domain is POSIX, the request will skip over it */ + mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "app_svc", false); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_simple_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_not_appsvc_app_dom(void **state) +{ + int ret; + + /* A different service than the app one can authenticate against a POSIX domain */ + mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, "not_app_svc", false); + mock_parse_inp("pamuser", NULL, EOK); + + pam_test_ctx->exp_pam_status = PAM_USER_UNKNOWN; + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_user_unknown_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +#define MY_PW_PROMPT "my_pw_prompt" +#define MY_2FA_SINGLE_PROMPT "my_2fa_single_prompt" +#define MY_FIRST_PROMPT "my_first_prompt" +#define MY_SECOND_PROMPT "my_second_prompt" +#define MY_PASSKEY_INTERACTIVE_PROMPT "my_passkey_interactive_prompt" +#define MY_PASSKEY_TOUCH_PROMPT "my_passkey_touch_prompt" +#define MY_SERVICE "my_service" + +static int pam_test_setup_pw_prompt(void **state) +{ + int ret; + + struct sss_test_conf_param prompt_params[] = { + { "password_prompt", MY_PW_PROMPT}, + { NULL, NULL }, /* Sentinel */ + }; + + ret = pam_test_setup(state); + assert_int_equal(ret, EOK); + + ret = add_confdb_params(prompt_params, pam_test_ctx->rctx->cdb, CONFDB_PC_CONF_ENTRY "/" CONFDB_PC_TYPE_PASSWORD); + assert_int_equal(ret, EOK); + + return 0; +} + +static int test_pam_prompt_check(uint32_t status, uint8_t *body, size_t blen) +{ + size_t rp = 0; + uint32_t val; + uint8_t val8t; + int ret; + struct prompt_config **pc = NULL; + + assert_int_equal(status, 0); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 3); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_PROMPT_CONFIG); + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + ret = pc_list_from_response(val, body + rp, &pc); + assert_int_equal(ret, EOK); + assert_non_null(pc[0]); + assert_int_equal(pc_get_type(pc[0]), pam_test_ctx->exp_prompt_config_type); + switch (pam_test_ctx->exp_prompt_config_type) { + case PC_TYPE_PASSWORD: + assert_string_equal(pc_get_password_prompt(pc[0]), MY_PW_PROMPT); + break; + case PC_TYPE_2FA_SINGLE: + assert_string_equal(pc_get_2fa_single_prompt(pc[0]), MY_2FA_SINGLE_PROMPT); + break; + case PC_TYPE_2FA: + assert_string_equal(pc_get_2fa_1st_prompt(pc[0]), MY_FIRST_PROMPT); + assert_string_equal(pc_get_2fa_2nd_prompt(pc[0]), MY_SECOND_PROMPT); + break; +#ifdef BUILD_PASSKEY + case PC_TYPE_PASSKEY: + assert_string_equal(pc_get_passkey_inter_prompt(pc[0]), MY_PASSKEY_INTERACTIVE_PROMPT); + assert_string_equal(pc_get_passkey_touch_prompt(pc[0]), pam_test_ctx->exp_touch_prompt); + break; +#endif + default: + assert_false(true); + } + assert_null(pc[1]); + pc_list_free(pc); + rp += val; + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + assert_string_equal((char *)(body + rp), TEST_DOM_NAME); + rp += val; + + + switch (pam_test_ctx->exp_prompt_config_type) { + case PC_TYPE_PASSWORD: + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PASSWORD_PROMPTING); + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 0); + break; + case PC_TYPE_2FA_SINGLE: + case PC_TYPE_2FA: + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_OTP_INFO); + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 3); + SAFEALIGN_COPY_UINT8_CHECK(&val8t, body + rp, blen, &rp); + assert_int_equal(val8t, 0); + SAFEALIGN_COPY_UINT8_CHECK(&val8t, body + rp, blen, &rp); + assert_int_equal(val8t, 0); + SAFEALIGN_COPY_UINT8_CHECK(&val8t, body + rp, blen, &rp); + assert_int_equal(val8t, 0); + break; +#ifdef BUILD_PASSKEY + case PC_TYPE_PASSKEY: + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_PASSKEY_INFO); + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 5); + rp += val; + break; +#endif + default: + assert_false(true); + } + + assert_int_equal(rp, blen); + + return EOK; +} + +void test_pam_prompting_password(void **state) +{ + int ret; + + pam_test_ctx->pctx->prompting_config_sections = NULL; + pam_test_ctx->pctx->num_prompting_config_sections = 0; + ret = confdb_get_sub_sections(pam_test_ctx->pctx, pam_test_ctx->pctx->rctx->cdb, CONFDB_PC_CONF_ENTRY, + &pam_test_ctx->pctx->prompting_config_sections, + &pam_test_ctx->pctx->num_prompting_config_sections); + assert_int_equal(ret, EOK); + + ret = pam_add_response(pam_test_ctx->pd, SSS_PASSWORD_PROMPTING, 0, NULL); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_prompt_config_type = PC_TYPE_PASSWORD; + set_cmd_cb(test_pam_prompt_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int pam_test_setup_2fa_single_prompt(void **state) +{ + int ret; + + struct sss_test_conf_param prompt_params[] = { + { "first_prompt", MY_2FA_SINGLE_PROMPT}, + { "single_prompt", "true"}, + { NULL, NULL }, /* Sentinel */ + }; + + ret = pam_test_setup(state); + assert_int_equal(ret, EOK); + + ret = add_confdb_params(prompt_params, pam_test_ctx->rctx->cdb, CONFDB_PC_CONF_ENTRY "/" CONFDB_PC_TYPE_2FA); + assert_int_equal(ret, EOK); + + return 0; +} + +void test_pam_prompting_2fa_single(void **state) +{ + int ret; + uint8_t otp_info[3] = { '\0' }; + + pam_test_ctx->pctx->prompting_config_sections = NULL; + pam_test_ctx->pctx->num_prompting_config_sections = 0; + ret = confdb_get_sub_sections(pam_test_ctx->pctx, pam_test_ctx->pctx->rctx->cdb, CONFDB_PC_CONF_ENTRY, + &pam_test_ctx->pctx->prompting_config_sections, + &pam_test_ctx->pctx->num_prompting_config_sections); + assert_int_equal(ret, EOK); + + ret = pam_add_response(pam_test_ctx->pd, SSS_PAM_OTP_INFO, 3, otp_info); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_prompt_config_type = PC_TYPE_2FA_SINGLE; + set_cmd_cb(test_pam_prompt_check); + + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int pam_test_setup_2fa_single_and_service_prompt(void **state) +{ + int ret; + + struct sss_test_conf_param prompt_service_params[] = { + { "first_prompt", MY_FIRST_PROMPT}, + { "second_prompt", MY_SECOND_PROMPT}, + { NULL, NULL }, /* Sentinel */ + }; + + ret = pam_test_setup_2fa_single_prompt(state); + assert_int_equal(ret, EOK); + + ret = add_confdb_params(prompt_service_params, pam_test_ctx->rctx->cdb, CONFDB_PC_CONF_ENTRY "/" CONFDB_PC_TYPE_2FA "/" MY_SERVICE); + assert_int_equal(ret, EOK); + + return 0; +} + +void test_pam_prompting_2fa_single_and_service_glob(void **state) +{ + int ret; + uint8_t otp_info[3] = { '\0' }; + + pam_test_ctx->pctx->prompting_config_sections = NULL; + pam_test_ctx->pctx->num_prompting_config_sections = 0; + ret = confdb_get_sub_sections(pam_test_ctx->pctx, pam_test_ctx->pctx->rctx->cdb, CONFDB_PC_CONF_ENTRY, + &pam_test_ctx->pctx->prompting_config_sections, + &pam_test_ctx->pctx->num_prompting_config_sections); + assert_int_equal(ret, EOK); + + ret = pam_add_response(pam_test_ctx->pd, SSS_PAM_OTP_INFO, 3, otp_info); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_prompt_config_type = PC_TYPE_2FA_SINGLE; + set_cmd_cb(test_pam_prompt_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_prompting_2fa_single_and_service_srv(void **state) +{ + int ret; + uint8_t otp_info[3] = { '\0' }; + + pam_test_ctx->pctx->prompting_config_sections = NULL; + pam_test_ctx->pctx->num_prompting_config_sections = 0; + ret = confdb_get_sub_sections(pam_test_ctx->pctx, pam_test_ctx->pctx->rctx->cdb, CONFDB_PC_CONF_ENTRY, + &pam_test_ctx->pctx->prompting_config_sections, + &pam_test_ctx->pctx->num_prompting_config_sections); + assert_int_equal(ret, EOK); + + ret = pam_add_response(pam_test_ctx->pd, SSS_PAM_OTP_INFO, 3, otp_info); + assert_int_equal(ret, EOK); + + mock_input_pam_ex(pam_test_ctx, "pamuser", NULL, NULL, MY_SERVICE, false); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_prompt_config_type = PC_TYPE_2FA; + set_cmd_cb(test_pam_prompt_check); + + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +#ifdef BUILD_PASSKEY +static int pam_test_setup_passkey_interactive_and_touch_prompt(void **state) +{ + int ret; + + struct sss_test_conf_param prompt_params[] = { + { "interactive", "true"}, + { "interactive_prompt", MY_PASSKEY_INTERACTIVE_PROMPT}, + { "touch", "true"}, + { "touch_prompt", MY_PASSKEY_TOUCH_PROMPT}, + { NULL, NULL }, /* Sentinel */ + }; + + ret = pam_test_setup(state); + assert_int_equal(ret, EOK); + + ret = add_confdb_params(prompt_params, pam_test_ctx->rctx->cdb, CONFDB_PC_CONF_ENTRY "/" CONFDB_PC_TYPE_PASSKEY); + assert_int_equal(ret, EOK); + + return 0; +} + +static int pam_test_setup_passkey_interactive_prompt(void **state) +{ + int ret; + + struct sss_test_conf_param prompt_params[] = { + { "interactive", "true"}, + { "interactive_prompt", MY_PASSKEY_INTERACTIVE_PROMPT}, + { NULL, NULL }, /* Sentinel */ + }; + + ret = pam_test_setup(state); + assert_int_equal(ret, EOK); + + ret = add_confdb_params(prompt_params, pam_test_ctx->rctx->cdb, CONFDB_PC_CONF_ENTRY "/" CONFDB_PC_TYPE_PASSKEY); + assert_int_equal(ret, EOK); + + return 0; +} + +void test_pam_prompting_passkey_interactive(void **state) +{ + int ret; + const char *prompt_pin = "true"; + + pam_test_ctx->pctx->prompting_config_sections = NULL; + pam_test_ctx->pctx->num_prompting_config_sections = 0; + ret = confdb_get_sub_sections(pam_test_ctx->pctx, pam_test_ctx->pctx->rctx->cdb, CONFDB_PC_CONF_ENTRY, + &pam_test_ctx->pctx->prompting_config_sections, + &pam_test_ctx->pctx->num_prompting_config_sections); + assert_int_equal(ret, EOK); + + ret = pam_add_response(pam_test_ctx->pd, SSS_PAM_PASSKEY_INFO, strlen(prompt_pin) + 1, + (const uint8_t *)prompt_pin); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_prompt_config_type = PC_TYPE_PASSKEY; + pam_test_ctx->exp_touch_prompt = ""; + set_cmd_cb(test_pam_prompt_check); + + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_prompting_passkey_interactive_and_touch(void **state) +{ + int ret; + const char *prompt_pin = "true"; + + pam_test_ctx->pctx->prompting_config_sections = NULL; + pam_test_ctx->pctx->num_prompting_config_sections = 0; + ret = confdb_get_sub_sections(pam_test_ctx->pctx, pam_test_ctx->pctx->rctx->cdb, CONFDB_PC_CONF_ENTRY, + &pam_test_ctx->pctx->prompting_config_sections, + &pam_test_ctx->pctx->num_prompting_config_sections); + assert_int_equal(ret, EOK); + + ret = pam_add_response(pam_test_ctx->pd, SSS_PAM_PASSKEY_INFO, strlen(prompt_pin) + 1, + (const uint8_t *)prompt_pin); + assert_int_equal(ret, EOK); + + mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_prompt_config_type = PC_TYPE_PASSKEY; + pam_test_ctx->exp_touch_prompt = MY_PASSKEY_TOUCH_PROMPT; + set_cmd_cb(test_pam_prompt_check); + + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_passkey_preauth_no_passkey(void **state) +{ + int ret; + + set_passkey_auth_param(pam_test_ctx->pctx); + + mock_input_pam_passkey(pam_test_ctx, "pamuser", "1234", + NULL, NULL, NULL); + + /* sss_parse_inp_recv() is called twice + * multiple cache req calls */ + mock_parse_inp("pamuser", NULL, EOK); + mock_parse_inp("pamuser", NULL, EOK); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + pam_test_ctx->exp_pam_status = PAM_SUCCESS; + set_cmd_cb(test_pam_passkey_preauth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_passkey_preauth_found(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + const char *passkey = SSSD_TEST_PASSKEY; + size_t pk_size; + const char *user_verification = "on"; + + set_passkey_auth_param(pam_test_ctx->pctx); + + /* Add user verification attribute */ + ret = sysdb_domain_update_passkey_user_verification( + pam_test_ctx->tctx->dom->sysdb, + pam_test_ctx->tctx->dom->name, + user_verification); + assert_int_equal(ret, EOK); + + mock_input_pam_passkey(pam_test_ctx, "pamuser", "1234", NULL, + NULL, SSSD_TEST_PASSKEY); + mock_parse_inp("pamuser", NULL, EOK); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + + /* Add the test passkey data for this user */ + pk_size = strlen(passkey) + 1; + + attrs = sysdb_new_attrs(pam_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_PASSKEY, passkey, pk_size); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + assert_int_equal(ret, EOK); + + pam_test_ctx->exp_pam_status = PAM_SUCCESS; + set_cmd_cb(test_pam_passkey_found_preauth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_passkey_auth(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + const char *passkey = SSSD_TEST_PASSKEY; + size_t pk_size; + const char *user_verification = "on"; + + set_passkey_auth_param(pam_test_ctx->pctx); + + /* Add user verification attribute */ + ret = sysdb_domain_update_passkey_user_verification( + pam_test_ctx->tctx->dom->sysdb, + pam_test_ctx->tctx->dom->name, + user_verification); + assert_int_equal(ret, EOK); + + mock_input_pam_passkey(pam_test_ctx, "pamuser", "1234", NULL, + NULL, SSSD_TEST_PASSKEY); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Add the test passkey data for this user */ + pk_size = strlen(passkey) + 1; + + attrs = sysdb_new_attrs(pam_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_PASSKEY, passkey, pk_size); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + assert_int_equal(ret, EOK); + + pam_test_ctx->exp_pam_status = PAM_SUCCESS; + set_cmd_cb(test_pam_passkey_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_passkey_bad_mapping(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + const char *pubkey = SSSD_TEST_PUBKEY; + size_t pk_size; + const char *user_verification = "on"; + + set_passkey_auth_param(pam_test_ctx->pctx); + + /* Add user verification attribute */ + ret = sysdb_domain_update_passkey_user_verification( + pam_test_ctx->tctx->dom->sysdb, + pam_test_ctx->tctx->dom->name, + user_verification); + assert_int_equal(ret, EOK); + + mock_input_pam_passkey(pam_test_ctx, "pamuser", "1234", NULL, + NULL, SSSD_TEST_PASSKEY); + mock_parse_inp("pamuser", NULL, EOK); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Add the test invalid pubkey data for this user */ + pk_size = strlen(pubkey) + 1; + + attrs = sysdb_new_attrs(pam_test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_PASSKEY, pubkey, pk_size); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + assert_int_equal(ret, EOK); + + pam_test_ctx->exp_pam_status = PAM_SUCCESS; + set_cmd_cb(test_pam_passkey_auth_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + + +void test_pam_passkey_auth_send(void **state) +{ + enum passkey_user_verification uv = PAM_PASSKEY_VERIFICATION_ON; + struct tevent_req *req; + struct pk_child_user_data *pk_data; + struct pam_items pi = { 0 }; + errno_t ret; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(global_talloc_context); + assert_non_null(tmp_ctx); + check_leaks_push(tmp_ctx); + + pk_data = talloc_zero(tmp_ctx, struct pk_child_user_data); + assert_non_null(pk_data); + pk_data->public_keys = talloc_zero_array(pk_data, const char *, 2); + pk_data->public_keys[0] = talloc_strdup(pk_data->public_keys, SSSD_TEST_PASSKEY_PK); + pk_data->key_handles = talloc_zero_array(pk_data, const char *, 2); + pk_data->key_handles[0] = talloc_strdup(pk_data->key_handles, SSSD_TEST_PASSKEY_KEY_HANDLE); + + /* pam data */ + pi.pam_authtok = discard_const("1234"); + pi.pam_authtok_size = strlen(pi.pam_authtok) + 1; + pi.pam_authtok_type = SSS_AUTHTOK_TYPE_PASSKEY; + pam_test_ctx->pd->user = discard_const("pamuser"); + pi.pam_user = "pamuser"; + pi.pam_user_size = strlen(pi.pam_user) + 1; + + req = pam_passkey_auth_send(tmp_ctx, pam_test_ctx->tctx->ev, + 10, false, uv, pam_test_ctx->pd, + pk_data, false); + assert_non_null(req); + tevent_req_set_callback(req, passkey_test_done, pam_test_ctx); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); + assert_true(WIFEXITED(pam_test_ctx->child_status)); + assert_int_equal(WEXITSTATUS(pam_test_ctx->child_status), 0); + + talloc_free(tmp_ctx); +} +#endif +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int res; + const char *single = NULL; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + { "no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a single test is run"), + NULL }, + { "list-tests", 'l', POPT_ARG_NONE, NULL, 'l', + _("Show all available tests"), + NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_pam_authenticate, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_setcreds, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_acct_mgmt, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_open_session, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_close_session, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_chauthtok, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_chauthtok_prelim, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_offline_auth_no_hash, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_offline_auth_success, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_offline_auth_wrong_pw, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_offline_auth_success_2fa, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_offline_auth_failed_2fa, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_offline_auth_success_2fa_with_cached_2fa, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_offline_auth_failed_2fa_with_cached_2fa, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_offline_auth_success_pw_with_cached_2fa, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_offline_auth_failed_pw_with_cached_2fa, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_offline_auth_success_combined_pw_with_cached_2fa, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_offline_auth_failed_combined_pw_with_cached_2fa, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_offline_auth_failed_wrong_2fa_size_with_cached_2fa, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_offline_chauthtok_prelim, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_offline_chauthtok, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_no_logon_name, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_auth_no_upn_logon_name, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_auth_upn_logon_name, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cached_auth_success, + pam_cached_test_setup, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cached_auth_wrong_pw, + pam_cached_test_setup, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cached_auth_opt_timeout, + pam_cached_test_setup, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cached_auth_timeout, + pam_cached_test_setup, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cached_auth_success_combined_pw_with_cached_2fa, + pam_cached_test_setup, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cached_auth_failed_combined_pw_with_cached_2fa, + pam_cached_test_setup, + pam_test_teardown), +#ifdef HAVE_TEST_CA + cmocka_unit_test_setup_teardown(test_pam_preauth_cert_nocert, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_cert_nomatch, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_cert_match, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_cert_match_gdm_smartcard, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_cert_match_wrong_user, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_cert_no_logon_name, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_preauth_cert_no_logon_name_with_hint, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_preauth_cert_no_logon_name_double_cert, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_preauth_cert_no_logon_name_double_cert_with_hint, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_no_cert_no_logon_name, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_preauth_cert_no_logon_name_no_match, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth, + pam_test_setup_no_verification, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth, + pam_test_setup_mech_rsa_pkcs, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth, + pam_test_setup_mech_rsa_sha384_pkcs, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_pss_cert_auth, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_ecc_cert_auth, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_intermediate_ca_cert_auth_with_full_certs, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_intermediate_ca_cert_auth_fails_with_incomplete_db, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_intermediate_ca_cert_auth_with_partial_chain, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_intermediate_ca_cert_auth_with_full_certs_and_partial_chain, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_intermediate_ca_cert_auth_fails_with_root_and_partial_chain, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown( + test_pam_intermediate_ca_cert_auth_with_partial_chain_pam_option, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth_double_cert, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_one_mapping, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_two_mappings, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_one_mapping, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_same_id_no_label, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_same_id_with_label_1, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth_2certs_same_id_with_label_6, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name_no_key_id, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_preauth_uri_token1, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_preauth_uri_token2, + pam_test_setup, pam_test_teardown), +#ifdef BUILD_PASSKEY + cmocka_unit_test_setup_teardown(test_pam_passkey_preauth_no_passkey, + pam_test_setup_passkey, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_passkey_preauth_found, + pam_test_setup_passkey, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_passkey_auth, + pam_test_setup_passkey, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_passkey_bad_mapping, + pam_test_setup_passkey, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_passkey_auth_send, + pam_test_setup_passkey, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_prompting_passkey_interactive, + pam_test_setup_passkey_interactive_prompt, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_prompting_passkey_interactive_and_touch, + pam_test_setup_passkey_interactive_and_touch_prompt, pam_test_teardown), +#endif /* BUILD_PASSKEY */ + +#ifdef HAVE_FAKETIME + cmocka_unit_test_setup_teardown(test_pam_preauth_expired_crl_file, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_expired_crl_file_soft, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_crl_valid_crl_invalid_files, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_crl_another_ca_crl_invalid_files, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_crl_invalid_crl_another_ca_files, + pam_test_setup, pam_test_teardown), +#endif /* HAVE_FAKETIME */ + cmocka_unit_test_setup_teardown(test_pam_preauth_ocsp, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_ocsp_no_ocsp, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_ocsp_soft_ocsp, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_first_crl_another_ca_files, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_preauth_last_crl_another_ca_files, + pam_test_setup, pam_test_teardown), +#endif /* HAVE_TEST_CA */ + + cmocka_unit_test_setup_teardown(test_filter_response, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_filter_response_defaults, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_appsvc_posix_dom, + pam_test_setup_appsvc_posix_dom, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_not_appsvc_posix_dom, + pam_test_setup_appsvc_posix_dom, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_appsvc_app_dom, + pam_test_setup_appsvc_app_dom, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_not_appsvc_app_dom, + pam_test_setup_appsvc_app_dom, + pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_prompting_password, + pam_test_setup_pw_prompt, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_prompting_2fa_single, + pam_test_setup_2fa_single_prompt, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_prompting_2fa_single_and_service_glob, + pam_test_setup_2fa_single_and_service_prompt, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_prompting_2fa_single_and_service_srv, + pam_test_setup_2fa_single_and_service_prompt, pam_test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + poptSetOtherOptionHelp(pc, "[OPTION...] [name_of_a_single_test]"); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'l': + fprintf(stderr, "\nAvailable tests:\n"); + list_tests(stderr, " -", tests, sizeof(tests)/sizeof(tests[0])); + return 0; + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + + single = poptGetArg(pc); + + if (single == NULL && no_cleanup) { + fprintf(stderr, "\nThe --no-cleanup makes only sense when running " + "a single test.\n\n"); + poptPrintUsage(pc, stderr, 0); + return 1; + } + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + + res = sss_cmocka_run_group_tests(tests, sizeof(tests)/sizeof(tests[0]), + single); + poptFreeContext(pc); + return res; +} diff --git a/src/tests/cmocka/test_passkey_child.c b/src/tests/cmocka/test_passkey_child.c new file mode 100644 index 0000000..5003152 --- /dev/null +++ b/src/tests/cmocka/test_passkey_child.c @@ -0,0 +1,1423 @@ +/* + SSSD + + Unit test helper child to commmunicate with passkey devices + + Authors: + Iker Pedrosa <ipedrosa@redhat.com> + + Copyright (C) 2022 Red Hat + + 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 <fido/es256.h> +#include <fido/param.h> +#include <popt.h> +#include <termios.h> + +#include "tests/cmocka/common_mock.h" + +#include "passkey_child/passkey_child.h" + +#define TEST_PATH "/test/path" +#define TEST_KEY_HANDLE "tOGNbhyeyiMJXzqPYbU8DT3Gxwk/LI7QajaW1sEhnNTDHFL5pT189IujIku03gwRJH/1tIKZ7Y8SvmfnOONd6g==" + +#define TEST_ES256_PEM_PUBLIC_KEY \ + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEA4ln5Oo8O4pLoSUCuYkXnzHxLiy" \ + "zpCYaysR/8JOJfpW/hMidzU50vqiHv6zLMSh16kntnXWfaGm319a9+8xrYQ==" +static const unsigned char TEST_ES256_HEX_PUBLIC_KEY[64] = { + 0x03, 0x89, 0x67, 0xe4, 0xea, 0x3c, 0x3b, 0x8a, + 0x4b, 0xa1, 0x25, 0x02, 0xb9, 0x89, 0x17, 0x9f, + 0x31, 0xf1, 0x2e, 0x2c, 0xb3, 0xa4, 0x26, 0x1a, + 0xca, 0xc4, 0x7f, 0xf0, 0x93, 0x89, 0x7e, 0x95, + 0xbf, 0x84, 0xc8, 0x9d, 0xcd, 0x4e, 0x74, 0xbe, + 0xa8, 0x87, 0xbf, 0xac, 0xcb, 0x31, 0x28, 0x75, + 0xea, 0x49, 0xed, 0x9d, 0x75, 0x9f, 0x68, 0x69, + 0xb7, 0xd7, 0xd6, 0xbd, 0xfb, 0xcc, 0x6b, 0x61, +}; + +#define TEST_RS256_PEM_PUBLIC_KEY \ + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnlR4slG+GXzLGprDSSo" \ + "v/Zlkdsbbyjg/sGrJwAefXE380QF/aWWrnCrCldlE8+qUayVmVIHuJB3hfX++6n" \ + "aQXL9ZItOgaBpliy+2qDAtJoH6nlnsL+5ZOeJ5GVRU3ySD7mFaZiQre/uCZuSFG" \ + "CB25Uq2y+xDvv2wj/0vadoGnAloepRst1FtTPcT6NUiax66uYXoX6Fm4yB1MBG1" \ + "o8Owcgj/o7vxMgsGxBKjSTAZuf5pDNbhWDbmQSJBv5ZQNVYNkow06iiRiJ6Kqjb" \ + "QD74W3p1fe9pS9/G2KBAFj7kZes8Ym0DN/3jqYSQ7gGgEm0AHmNSU0RhEpe3uGM" \ + "IlUmZC3wIDAQAB" +static const unsigned char TEST_RS256_HEX_PUBLIC_KEY[259] = { + 0x9e, 0x54, 0x78, 0xb2, 0x51, 0xbe, 0x19, 0x7c, + 0xcb, 0x1a, 0x9a, 0xc3, 0x49, 0x2a, 0x2f, 0xfd, + 0x99, 0x64, 0x76, 0xc6, 0xdb, 0xca, 0x38, 0x3f, + 0xb0, 0x6a, 0xc9, 0xc0, 0x07, 0x9f, 0x5c, 0x4d, + 0xfc, 0xd1, 0x01, 0x7f, 0x69, 0x65, 0xab, 0x9c, + 0x2a, 0xc2, 0x95, 0xd9, 0x44, 0xf3, 0xea, 0x94, + 0x6b, 0x25, 0x66, 0x54, 0x81, 0xee, 0x24, 0x1d, + 0xe1, 0x7d, 0x7f, 0xbe, 0xea, 0x76, 0x90, 0x5c, + 0xbf, 0x59, 0x22, 0xd3, 0xa0, 0x68, 0x1a, 0x65, + 0x8b, 0x2f, 0xb6, 0xa8, 0x30, 0x2d, 0x26, 0x81, + 0xfa, 0x9e, 0x59, 0xec, 0x2f, 0xee, 0x59, 0x39, + 0xe2, 0x79, 0x19, 0x54, 0x54, 0xdf, 0x24, 0x83, + 0xee, 0x61, 0x5a, 0x66, 0x24, 0x2b, 0x7b, 0xfb, + 0x82, 0x66, 0xe4, 0x85, 0x18, 0x20, 0x76, 0xe5, + 0x4a, 0xb6, 0xcb, 0xec, 0x43, 0xbe, 0xfd, 0xb0, + 0x8f, 0xfd, 0x2f, 0x69, 0xda, 0x06, 0x9c, 0x09, + 0x68, 0x7a, 0x94, 0x6c, 0xb7, 0x51, 0x6d, 0x4c, + 0xf7, 0x13, 0xe8, 0xd5, 0x22, 0x6b, 0x1e, 0xba, + 0xb9, 0x85, 0xe8, 0x5f, 0xa1, 0x66, 0xe3, 0x20, + 0x75, 0x30, 0x11, 0xb5, 0xa3, 0xc3, 0xb0, 0x72, + 0x08, 0xff, 0xa3, 0xbb, 0xf1, 0x32, 0x0b, 0x06, + 0xc4, 0x12, 0xa3, 0x49, 0x30, 0x19, 0xb9, 0xfe, + 0x69, 0x0c, 0xd6, 0xe1, 0x58, 0x36, 0xe6, 0x41, + 0x22, 0x41, 0xbf, 0x96, 0x50, 0x35, 0x56, 0x0d, + 0x92, 0x8c, 0x34, 0xea, 0x28, 0x91, 0x88, 0x9e, + 0x8a, 0xaa, 0x36, 0xd0, 0x0f, 0xbe, 0x16, 0xde, + 0x9d, 0x5f, 0x7b, 0xda, 0x52, 0xf7, 0xf1, 0xb6, + 0x28, 0x10, 0x05, 0x8f, 0xb9, 0x19, 0x7a, 0xcf, + 0x18, 0x9b, 0x40, 0xcd, 0xff, 0x78, 0xea, 0x61, + 0x24, 0x3b, 0x80, 0x68, 0x04, 0x9b, 0x40, 0x07, + 0x98, 0xd4, 0x94, 0xd1, 0x18, 0x44, 0xa5, 0xed, + 0xee, 0x18, 0xc2, 0x25, 0x52, 0x66, 0x42, 0xdf, + 0x01, 0x00, 0x01 +}; + +#define TEST_EDDSA_PEM_PUBLIC_KEY \ + "MCowBQYDK2VwAyEAr9oDMRm0bGxFmcfNPzlD05i3nFnX71lVl2b4Q2OAia4=" +static const unsigned char TEST_EDDSA_HEX_PUBLIC_KEY[32] = { + 0xaf, 0xda, 0x03, 0x31, 0x19, 0xb4, 0x6c, 0x6c, + 0x45, 0x99, 0xc7, 0xcd, 0x3f, 0x39, 0x43, 0xd3, + 0x98, 0xb7, 0x9c, 0x59, 0xd7, 0xef, 0x59, 0x55, + 0x97, 0x66, 0xf8, 0x43, 0x63, 0x80, 0x89, 0xae +}; + +#define TEST_CRYPTO_CHALLENGE "mZmBWUaJGwEjSNQvkFaicpCzDKhap2pQlfi8FXsv68k=" + +#define TEST_B64_AUTH_DATA "authdata" +#define TEST_AUTH_DATA_LEN 6 +static const unsigned char TEST_HEX_AUTH_DATA[TEST_AUTH_DATA_LEN] = { + 0x6a, 0xeb, 0x61, 0x75, 0xab, 0x5a +}; + +#define TEST_B64_SIGNATURE "signatur" +#define TEST_SIGNATURE_LEN 6 +static const unsigned char TEST_HEX_SIGNATURE[TEST_SIGNATURE_LEN] = { + 0xb2, 0x28, 0x27, 0x6a, 0xdb, 0xab +}; + +struct test_state { + fido_cred_t *cred; + struct passkey_data data; + fido_dev_info_t *dev_list; + size_t dev_number; + fido_dev_t *dev; + fido_assert_t *assert; +}; + +/*********************** + * SETUP AND TEARDOWN + **********************/ +static int setup(void **state) +{ + struct test_state *ts = NULL; + + assert_true(leak_check_setup()); + + ts = talloc(global_talloc_context, struct test_state); + assert_non_null(ts); + + ts->cred = fido_cred_new(); + assert_non_null(ts->cred); + ts->data.shortname = "user"; + ts->data.domain = "test.com"; + ts->data.type = COSE_ES256; + ts->data.user_id = talloc_array(global_talloc_context, unsigned char, + USER_ID_SIZE); + assert_non_null(ts->data.user_id); + + ts->dev_list = fido_dev_info_new(DEVLIST_SIZE); + assert_non_null(ts->dev_list); + ts->dev_number = 0; + + ts->dev = fido_dev_new(); + assert_non_null(ts->dev); + + ts->assert = fido_assert_new(); + assert_non_null(ts->assert); + + check_leaks_push(ts); + *state = (void *)ts; + return 0; +} + +static int teardown(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + + assert_non_null(ts); + talloc_free(ts->data.user_id); + + assert_true(check_leaks_pop(ts)); + fido_cred_free(&ts->cred); + fido_dev_info_free(&ts->dev_list, ts->dev_number); + if (ts->dev != NULL) { + fido_dev_close(ts->dev); + } + fido_dev_free(&ts->dev); + fido_assert_free(&ts->assert); + talloc_free(ts); + assert_true(leak_check_teardown()); + return 0; +} + +/*********************** + * WRAPPERS + **********************/ +unsigned int +__wrap_sleep(unsigned int seconds) +{ + int ret; + + ret = mock(); + + return ret; +} + +int +__wrap_tcgetattr(int fd, struct termios *termios_p) +{ + int ret; + + ret = mock(); + + return ret; +} + +int +__wrap_tcsetattr(int fd, int optional_actions, + const struct termios *termios_p) +{ + int ret; + + ret = mock(); + + return ret; +} + +ssize_t +__wrap_getline(char **restrict lineptr, size_t *restrict n, + FILE *restrict stream) +{ + ssize_t ret; + + ret = (ssize_t) mock(); + (*lineptr) = (char *) mock(); + + return ret; +} + +int +__wrap_fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, + size_t *olen) +{ + int ret; + + ret = mock(); + (*olen) = mock(); + + return ret; +} + +const char * +__wrap_fido_dev_info_path(const fido_dev_info_t *di) +{ + const char *ret; + + ret = (const char *) mock(); + + return ret; +} + +int +__wrap_fido_dev_open(fido_dev_t *dev, const char *path) +{ + int ret; + + ret = mock(); + + return ret; +} + +bool +__wrap_fido_dev_has_uv(fido_dev_t *dev) +{ + bool ret; + + ret = mock(); + + return ret; +} + +bool +__wrap_fido_dev_has_pin(fido_dev_t *dev) +{ + bool ret; + + ret = mock(); + + return ret; +} + +bool +__wrap_fido_dev_supports_uv(fido_dev_t *dev) +{ + bool ret; + + ret = mock(); + + return ret; +} + +int +__wrap_fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin) +{ + int ret; + + ret = mock(); + + return ret; +} + +const unsigned char * +__wrap_fido_cred_x5c_ptr(fido_cred_t *cred) +{ + const unsigned char *ret; + + ret = (const unsigned char *) mock(); + + return ret; +} + +int +__wrap_fido_cred_verify(fido_cred_t *cred) +{ + int ret; + + ret = mock(); + + return ret; +} + +int +__wrap_fido_cred_verify_self(fido_cred_t *cred) +{ + int ret; + + ret = mock(); + + return ret; +} + +const unsigned char * +__wrap_fido_cred_id_ptr(const fido_cred_t *cred) +{ + const unsigned char *ret; + + ret = (const unsigned char *) mock(); + + return ret; +} + +int +__wrap_fido_cred_id_len(fido_cred_t *cred) +{ + int ret; + + ret = mock(); + + return ret; +} + +const unsigned char * +__wrap_fido_cred_pubkey_ptr(const fido_cred_t *cred) +{ + const unsigned char *ret; + + ret = (const unsigned char *) mock(); + + return ret; +} + +int +__wrap_fido_cred_pubkey_len(fido_cred_t *cred) +{ + int ret; + + ret = mock(); + + return ret; +} + +int +__wrap_fido_assert_set_rp(fido_assert_t *assert, const char *id) +{ + int ret; + + ret = mock(); + + return ret; +} + +int +__wrap_fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr, + size_t len) +{ + int ret; + + ret = mock(); + + return ret; +} + +int +__wrap_fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv) +{ + int ret; + + ret = mock(); + + return ret; +} + +size_t +__wrap_fido_assert_user_id_len(const fido_assert_t *assert, size_t size) +{ + size_t ret; + + ret = (size_t) mock(); + + return ret; +} + +int +__wrap_fido_assert_set_clientdata_hash(fido_assert_t *assert, + const unsigned char *ptr, size_t len) +{ + int ret; + + ret = mock(); + + return ret; +} + +int +__wrap_fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, + const char *pin) +{ + int ret; + + ret = mock(); + + return ret; +} + +bool +__wrap_fido_dev_is_fido2(const fido_dev_t *dev) +{ + bool ret; + + ret = mock(); + + return ret; +} + +int +__wrap_fido_assert_verify(const fido_assert_t *assert, size_t idx, + int cose_alg, const void *pk) +{ + int ret; + + ret = mock(); + + return ret; +} + +const unsigned char * +__wrap_fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx) +{ + const unsigned char *ret; + + ret = (const unsigned char *) mock(); + + return ret; +} + +size_t +__wrap_fido_assert_authdata_len(const fido_assert_t *assert, size_t idx) +{ + size_t ret; + + ret = (size_t) mock(); + + return ret; +} + +const unsigned char * +__wrap_fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx) +{ + const unsigned char *ret; + + ret = (const unsigned char *) mock(); + + return ret; +} + +size_t +__wrap_fido_assert_sig_len(const fido_assert_t *assert, size_t idx) +{ + size_t ret; + + ret = (size_t) mock(); + + return ret; +} + +int +__wrap_fido_assert_set_count(fido_assert_t *assert, size_t n) +{ + int ret; + + ret = mock(); + + return ret; +} + +int +__wrap_fido_assert_set_authdata(fido_assert_t *assert, size_t idx, + const unsigned char *ptr, size_t len) +{ + int ret; + + ret = mock(); + + return ret; +} + +int +__wrap_fido_assert_set_sig(fido_assert_t *assert, size_t idx, + const unsigned char *ptr, size_t len) +{ + int ret; + + ret = mock(); + + return ret; +} + +/*********************** + * TEST + **********************/ +void test_parse_required_args(void **state) +{ + TALLOC_CTX *test_ctx; + struct passkey_data data; + int argc = 0; + const char *argv[19] = { NULL }; + errno_t ret; + + test_ctx = talloc_new(NULL); + assert_non_null(test_ctx); + argv[argc++] = "passkey_child"; + argv[argc++] = "--register"; + argv[argc++] = "--username=user"; + argv[argc++] = "--domain=test.com"; + argv[argc++] = "--public-key=publicKey"; + argv[argc++] = "--key-handle=keyHandle"; + + ret = parse_arguments(test_ctx, argc, argv, &data); + + assert_int_equal(ret, 0); + assert_int_equal(data.action, ACTION_REGISTER); + assert_string_equal(data.shortname, "user"); + assert_string_equal(data.domain, "test.com"); + assert_string_equal(data.public_key_list[0], "publicKey"); + assert_string_equal(data.key_handle_list[0], "keyHandle"); + assert_int_equal(data.type, COSE_ES256); + assert_int_equal(data.user_verification, FIDO_OPT_OMIT); + assert_int_equal(data.cred_type, CRED_SERVER_SIDE); + assert_int_equal(data.debug_libfido2, false); + + talloc_free(test_ctx); +} + +void test_parse_all_args(void **state) +{ + TALLOC_CTX *test_ctx; + struct passkey_data data; + int argc = 0; + const char *argv[19] = { NULL }; + errno_t ret; + + test_ctx = talloc_new(NULL); + assert_non_null(test_ctx); + argv[argc++] = "passkey_child"; + argv[argc++] = "--authenticate"; + argv[argc++] = "--username=user"; + argv[argc++] = "--domain=test.com"; + argv[argc++] = "--public-key=publicKey"; + argv[argc++] = "--key-handle=keyHandle"; + argv[argc++] = "--cryptographic-challenge=crypto"; + argv[argc++] = "--auth-data=auth"; + argv[argc++] = "--signature=sign"; + argv[argc++] = "--type=rs256"; + argv[argc++] = "--user-verification=true"; + argv[argc++] = "--cred-type=discoverable"; + argv[argc++] = "--debug-libfido2"; + + ret = parse_arguments(test_ctx, argc, argv, &data); + + assert_int_equal(ret, 0); + assert_int_equal(data.action, ACTION_AUTHENTICATE); + assert_string_equal(data.shortname, "user"); + assert_string_equal(data.domain, "test.com"); + assert_string_equal(data.public_key_list[0], "publicKey"); + assert_string_equal(data.key_handle_list[0], "keyHandle"); + assert_string_equal(data.crypto_challenge, "crypto"); + assert_string_equal(data.auth_data, "auth"); + assert_string_equal(data.signature, "sign"); + assert_int_equal(data.type, COSE_RS256); + assert_int_equal(data.user_verification, FIDO_OPT_TRUE); + assert_int_equal(data.cred_type, CRED_DISCOVERABLE); + assert_int_equal(data.debug_libfido2, true); + + talloc_free(test_ctx); +} + +void test_prepare_credentials_ok(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + ts->data.user_verification = FIDO_OPT_TRUE; + ts->data.cred_type = CRED_SERVER_SIDE; + will_return(__wrap_fido_dev_has_uv, true); + will_return(__wrap_fido_dev_has_pin, false); + + ret = prepare_credentials(&ts->data, ts->dev, ts->cred); + + assert_int_equal(ret, FIDO_OK); + assert_string_equal(fido_cred_user_name(ts->cred), "user"); + assert_string_equal(fido_cred_rp_id(ts->cred), "test.com"); + assert_string_equal(fido_cred_rp_name(ts->cred), "test.com"); + assert_int_equal(fido_cred_type(ts->cred), COSE_ES256); +} + +void test_prepare_credentials_user_verification_missing(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + ts->data.user_verification = FIDO_OPT_TRUE; + ts->data.cred_type = CRED_SERVER_SIDE; + will_return(__wrap_fido_dev_has_uv, false); + will_return(__wrap_fido_dev_has_pin, false); + + ret = prepare_credentials(&ts->data, ts->dev, ts->cred); + + assert_int_equal(ret, EINVAL); +} + +void test_prepare_credentials_error(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + // Set the type to an incorrect value intentionally + ts->data.type = 0; + + ret = prepare_credentials(&ts->data, ts->dev, ts->cred); + + assert_int_equal(ret, FIDO_ERR_INVALID_ARGUMENT); +} + +void test_list_devices_one_device(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + will_return(__wrap_fido_dev_info_manifest, FIDO_OK); + will_return(__wrap_fido_dev_info_manifest, 1); + + ret = list_devices(ts->dev_list, &ts->dev_number); + + assert_int_equal(ret, FIDO_OK); + assert_int_equal(ts->dev_number, 1); +} + +void test_list_devices_no_device(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + for (int i = 0; i < TIMEOUT; i += FREQUENCY) { + will_return(__wrap_fido_dev_info_manifest, FIDO_OK); + will_return(__wrap_fido_dev_info_manifest, 0); + if (i < (TIMEOUT - 1)) { + will_return(__wrap_sleep, 0); + } + } + + ret = list_devices(ts->dev_list, &ts->dev_number); + + assert_int_equal(ret, FIDO_OK); + assert_int_equal(ts->dev_number, 0); +} + +void test_list_devices_error(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + for (int i = 0; i < TIMEOUT; i += FREQUENCY) { + will_return(__wrap_fido_dev_info_manifest, FIDO_ERR_INVALID_ARGUMENT); + will_return(__wrap_fido_dev_info_manifest, 0); + if (i < (TIMEOUT - 1)) { + will_return(__wrap_sleep, 0); + } + } + + ret = list_devices(ts->dev_list, &ts->dev_number); + + assert_int_equal(ret, FIDO_ERR_INVALID_ARGUMENT); +} + +void test_select_device_found(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + fido_dev_t *dev = NULL; + errno_t ret; + + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + + ret = select_device(ACTION_REGISTER, ts->dev_list, 1, NULL, &dev); + + assert_int_equal(ret, FIDO_OK); + fido_dev_close(dev); + fido_dev_free(&dev); +} + +void test_select_device_open_failed(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + fido_dev_t *dev = NULL; + errno_t ret; + + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_ERR_INVALID_ARGUMENT); + + ret = select_device(ACTION_REGISTER, ts->dev_list, 1, NULL, &dev); + + assert_int_equal(ret, FIDO_ERR_INVALID_ARGUMENT); +} + +void test_read_pass(void **state) +{ + ssize_t test_len = 6; + char *pin = NULL; + char *expected_pin = malloc(test_len); + ssize_t bytes_read; + + snprintf(expected_pin, test_len, "%s\n", "1234"); + will_return(__wrap_tcgetattr, 0); + will_return(__wrap_tcsetattr, 0); + will_return(__wrap_getline, test_len); + will_return(__wrap_getline, expected_pin); + will_return(__wrap_tcsetattr, 0); + + bytes_read = read_pin(&pin); + + assert_int_equal(bytes_read, test_len - 1); + assert_string_equal(pin, expected_pin); + + free(expected_pin); +} + +void test_generate_credentials_user_verification(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + ts->data.quiet = false; + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_make_cred, FIDO_OK); + + ret = generate_credentials(&ts->data, ts->dev, ts->cred); + + assert_int_equal(ret, FIDO_OK); +} + +void test_generate_credentials_pin(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + ssize_t test_len = 6; + char *pin = malloc(test_len); + errno_t ret; + + ts->data.quiet = false; + snprintf(pin, test_len, "%s\n", "1234"); + will_return(__wrap_fido_dev_has_pin, true); + will_return(__wrap_tcgetattr, 0); + will_return(__wrap_tcsetattr, 0); + will_return(__wrap_getline, test_len); + will_return(__wrap_getline, pin); + will_return(__wrap_tcsetattr, 0); + will_return(__wrap_fido_dev_make_cred, FIDO_OK); + + ret = generate_credentials(&ts->data, ts->dev, ts->cred); + + assert_int_equal(ret, FIDO_OK); +} + +void test_generate_credentials_pin_error(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + char *expected_pin; + errno_t ret; + + ts->data.quiet = false; + will_return(__wrap_fido_dev_has_pin, true); + will_return(__wrap_tcgetattr, 0); + will_return(__wrap_tcsetattr, 0); + will_return(__wrap_getline, -1); + will_return(__wrap_getline, expected_pin); + will_return(__wrap_tcsetattr, 0); + + ret = generate_credentials(&ts->data, ts->dev, ts->cred); + + assert_int_equal(ret, ERR_INPUT_PARSE); +} + +void test_verify_credentials_basic_attestation(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + will_return(__wrap_fido_cred_x5c_ptr, "mock"); + will_return(__wrap_fido_cred_verify, FIDO_OK); + + ret = verify_credentials(ts->cred); + + assert_int_equal(ret, FIDO_OK); +} + +void test_verify_credentials_self_attestation(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + will_return(__wrap_fido_cred_x5c_ptr, NULL); + will_return(__wrap_fido_cred_verify_self, FIDO_OK); + + ret = verify_credentials(ts->cred); + + assert_int_equal(ret, FIDO_OK); +} + +void test_verify_credentials_error(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + will_return(__wrap_fido_cred_x5c_ptr, "mock"); + will_return(__wrap_fido_cred_verify, FIDO_ERR_INVALID_ARGUMENT); + + ret = verify_credentials(ts->cred); + + assert_int_equal(ret, FIDO_ERR_INVALID_ARGUMENT); +} + +void test_decode_public_key(void **state) +{ + TALLOC_CTX *test_ctx = NULL; + struct passkey_data data; + char *pem_key = NULL; + errno_t ret; + + test_ctx = talloc_new(NULL); + assert_non_null(test_ctx); + + data.type = COSE_ES256; + ret = public_key_to_base64(test_ctx, &data, TEST_ES256_HEX_PUBLIC_KEY, 64, + &pem_key); + assert_int_equal(ret, EOK); + assert_string_equal(pem_key, TEST_ES256_PEM_PUBLIC_KEY); + + data.type = COSE_RS256; + ret = public_key_to_base64(test_ctx, &data, TEST_RS256_HEX_PUBLIC_KEY, 259, + &pem_key); + assert_int_equal(ret, EOK); + assert_string_equal(pem_key, TEST_RS256_PEM_PUBLIC_KEY); + + data.type = COSE_EDDSA; + ret = public_key_to_base64(test_ctx, &data, TEST_EDDSA_HEX_PUBLIC_KEY, 32, + &pem_key); + assert_int_equal(ret, EOK); + assert_string_equal(pem_key, TEST_EDDSA_PEM_PUBLIC_KEY); + + talloc_free(test_ctx); +} + +void test_register_key_integration(void **state) +{ + struct passkey_data data; + const char *credential_id = "credential_id"; + errno_t ret; + + data.action = ACTION_REGISTER; + data.shortname = "user"; + data.domain = "test.com"; + data.type = COSE_ES256; + data.user_verification = FIDO_OPT_FALSE; + data.cred_type = CRED_SERVER_SIDE; + data.mapping_file = NULL; + data.quiet = false; + will_return(__wrap_fido_dev_info_manifest, FIDO_OK); + will_return(__wrap_fido_dev_info_manifest, 1); + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + will_return(__wrap_fido_dev_has_uv, false); + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_make_cred, FIDO_OK); + will_return(__wrap_fido_cred_x5c_ptr, "mock"); + will_return(__wrap_fido_cred_verify, FIDO_OK); + will_return(__wrap_fido_cred_id_ptr, credential_id); + will_return(__wrap_fido_cred_id_len, strlen(credential_id)); + will_return(__wrap_fido_cred_pubkey_ptr, TEST_ES256_HEX_PUBLIC_KEY); + will_return(__wrap_fido_cred_pubkey_len, 64); + + ret = register_key(&data); + + assert_int_equal(ret, EOK); +} + +void test_select_authenticator(void **state) +{ + TALLOC_CTX *tmp_ctx; + struct passkey_data data; + fido_dev_t *dev = NULL; + fido_assert_t *assert = NULL; + int index = 0; + char *key_handle; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + data.action = ACTION_GET_ASSERT; + data.domain = "test.com"; + key_handle = talloc_strdup(tmp_ctx, TEST_KEY_HANDLE); + data.key_handle_list = &key_handle; + data.key_handle_size = 1; + data.crypto_challenge = TEST_CRYPTO_CHALLENGE; + will_return(__wrap_fido_dev_info_manifest, FIDO_OK); + will_return(__wrap_fido_dev_info_manifest, 1); + will_return(__wrap_fido_assert_set_rp, FIDO_OK); + will_return(__wrap_fido_assert_allow_cred, FIDO_OK); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + will_return(__wrap_fido_assert_set_clientdata_hash, FIDO_OK); + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + will_return(__wrap_fido_dev_is_fido2, true); + will_return(__wrap_fido_dev_get_assert, FIDO_OK); + + ret = select_authenticator(&data, &dev, &assert, &index); + + assert_int_equal(ret, FIDO_OK); + + if (dev != NULL) { + fido_dev_close(dev); + } + fido_dev_free(&dev); + fido_assert_free(&assert); + talloc_free(tmp_ctx); +} + +void test_prepare_assert_ok(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + ts->data.action = ACTION_AUTHENTICATE; + ts->data.key_handle_list = talloc_array(ts, char*, 1); + ts->data.key_handle_list[0] = talloc_strdup(ts, "a2V5SGFuZGxl"); + will_return(__wrap_fido_assert_set_rp, FIDO_OK); + will_return(__wrap_fido_assert_allow_cred, FIDO_OK); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + will_return(__wrap_fido_assert_set_clientdata_hash, FIDO_OK); + + ret = prepare_assert(&ts->data, 0, ts->assert); + + assert_int_equal(ret, FIDO_OK); + talloc_free(ts->data.key_handle_list[0]); + talloc_free(ts->data.key_handle_list); +} + +void test_prepare_assert_error(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + ts->data.action = ACTION_AUTHENTICATE; + ts->data.key_handle_list = talloc_array(ts, char*, 1); + ts->data.key_handle_list[0] = talloc_strdup(ts, "a2V5SGFuZGxl"); + will_return(__wrap_fido_assert_set_rp, FIDO_ERR_INVALID_ARGUMENT); + + ret = prepare_assert(&ts->data, 0, ts->assert); + + assert_int_equal(ret, FIDO_ERR_INVALID_ARGUMENT); + talloc_free(ts->data.key_handle_list[0]); + talloc_free(ts->data.key_handle_list); +} + +void test_reset_public_key(void **state) +{ + struct pk_data_t pk_data; + errno_t ret; + + pk_data.type = COSE_ES256; + pk_data.public_key = es256_pk_new(); + + ret = reset_public_key(&pk_data); + + assert_int_equal(ret, EOK); +} + +void test_encode_public_keys(void **state) +{ + struct pk_data_t pk_data; + errno_t ret; + + ret = public_key_to_libfido2(TEST_ES256_PEM_PUBLIC_KEY, &pk_data); + assert_int_equal(ret, FIDO_OK); + assert_memory_equal(pk_data.public_key, TEST_ES256_HEX_PUBLIC_KEY, 64); + reset_public_key(&pk_data); + + ret = public_key_to_libfido2(TEST_RS256_PEM_PUBLIC_KEY, &pk_data); + assert_int_equal(ret, FIDO_OK); + assert_memory_equal(pk_data.public_key, TEST_RS256_HEX_PUBLIC_KEY, 259); + reset_public_key(&pk_data); + + ret = public_key_to_libfido2(TEST_EDDSA_PEM_PUBLIC_KEY, &pk_data); + assert_int_equal(ret, FIDO_OK); + assert_memory_equal(pk_data.public_key, TEST_EDDSA_HEX_PUBLIC_KEY, 32); + reset_public_key(&pk_data); +} + +void test_get_authenticator_data_one_key(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + fido_dev_t *dev = NULL; + errno_t ret; + + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + will_return(__wrap_fido_dev_is_fido2, true); + will_return(__wrap_fido_dev_get_assert, FIDO_OK); + + ret = select_device(ACTION_AUTHENTICATE, ts->dev_list, 1, ts->assert, &dev); + + assert_int_equal(ret, FIDO_OK); + assert_non_null(dev); + fido_dev_close(dev); + fido_dev_free(&dev); +} + +void test_get_authenticator_data_multiple_keys_assert_found(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + fido_dev_t *dev = NULL; + errno_t ret; + + // Key 1 + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + will_return(__wrap_fido_dev_is_fido2, true); + will_return(__wrap_fido_dev_get_assert, FIDO_ERR_INVALID_SIG); + // Key 2 + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + will_return(__wrap_fido_dev_is_fido2, true); + will_return(__wrap_fido_dev_get_assert, FIDO_OK); + + ret = select_device(ACTION_AUTHENTICATE, ts->dev_list, 2, ts->assert, &dev); + + assert_int_equal(ret, FIDO_OK); + assert_non_null(dev); + fido_dev_close(dev); + fido_dev_free(&dev); +} + +void test_get_authenticator_data_multiple_keys_assert_not_found(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + fido_dev_t *dev = NULL; + errno_t ret; + + // Key 1 + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + will_return(__wrap_fido_dev_is_fido2, true); + will_return(__wrap_fido_dev_get_assert, FIDO_ERR_INVALID_SIG); + // Key 2 + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + will_return(__wrap_fido_dev_is_fido2, true); + will_return(__wrap_fido_dev_get_assert, FIDO_ERR_INVALID_SIG); + + ret = select_device(ACTION_AUTHENTICATE, ts->dev_list, 2, ts->assert, &dev); + + assert_int_equal(ret, FIDO_ERR_NOTFOUND); + assert_null(dev); +} + +void test_get_device_options(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + fido_dev_t *dev = NULL; + errno_t ret; + + ts->data.user_verification = FIDO_OPT_TRUE; + will_return(__wrap_fido_dev_has_uv, true); + will_return(__wrap_fido_dev_has_pin, true); + will_return(__wrap_fido_dev_supports_uv, true); + + ret = get_device_options(dev, &ts->data); + + assert_int_equal(ret, FIDO_OK); + assert_int_equal(ts->data.user_verification, FIDO_OPT_TRUE); +} + +void test_get_device_options_user_verification_unmatch(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + fido_dev_t *dev = NULL; + errno_t ret; + + ts->data.user_verification = FIDO_OPT_TRUE; + will_return(__wrap_fido_dev_has_uv, false); + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_supports_uv, false); + + ret = get_device_options(dev, &ts->data); + + assert_int_equal(ret, EINVAL); +} + +void test_get_device_options_user_verification_false_not_supported(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + fido_dev_t *dev = NULL; + errno_t ret; + + ts->data.user_verification = FIDO_OPT_FALSE; + will_return(__wrap_fido_dev_has_uv, false); + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_supports_uv, false); + + ret = get_device_options(dev, &ts->data); + + assert_int_equal(ret, FIDO_OK); + assert_int_equal(ts->data.user_verification, FIDO_OPT_OMIT); +} + +void test_request_assert(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + errno_t ret; + + ts->data.user_verification = FIDO_OPT_FALSE; + will_return(__wrap_fido_dev_has_uv, false); + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_get_assert, FIDO_OK); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + + ret = request_assert(&ts->data, ts->dev, ts->assert); + + assert_int_equal(ret, FIDO_OK); +} + +void test_verify_assert(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + struct pk_data_t pk_data; + errno_t ret; + + pk_data.type = COSE_ES256; + pk_data.public_key = es256_pk_new(); + ts->data.user_verification = FIDO_OPT_FALSE; + will_return(__wrap_fido_assert_verify, FIDO_OK); + + ret = verify_assert(&pk_data, ts->assert); + + assert_int_equal(ret, FIDO_OK); + + reset_public_key(&pk_data); +} + +void test_verify_assert_failed(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + struct pk_data_t pk_data; + errno_t ret; + + pk_data.type = COSE_ES256; + pk_data.public_key = es256_pk_new(); + ts->data.user_verification = FIDO_OPT_FALSE; + will_return(__wrap_fido_assert_verify, FIDO_ERR_TX); + + ret = verify_assert(&pk_data, ts->assert); + + assert_int_equal(ret, FIDO_ERR_TX); + + reset_public_key(&pk_data); +} + +void test_authenticate_integration(void **state) +{ + TALLOC_CTX *tmp_ctx; + struct passkey_data data; + size_t dev_number = 3; + char *key_handle; + char *public_key; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + data.action = ACTION_AUTHENTICATE; + data.shortname = "user"; + data.domain = "test.com"; + key_handle = talloc_strdup(tmp_ctx, TEST_KEY_HANDLE); + public_key = talloc_strdup(tmp_ctx, TEST_ES256_PEM_PUBLIC_KEY); + data.key_handle_list = &key_handle; + data.key_handle_size = 1; + data.public_key_list = &public_key; + data.public_key_size = 1; + data.type = COSE_ES256; + data.user_verification = FIDO_OPT_FALSE; + data.user_id = NULL; + data.quiet = false; + will_return(__wrap_fido_dev_info_manifest, FIDO_OK); + will_return(__wrap_fido_dev_info_manifest, dev_number); + will_return(__wrap_fido_assert_set_rp, FIDO_OK); + will_return(__wrap_fido_assert_allow_cred, FIDO_OK); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + will_return(__wrap_fido_assert_set_clientdata_hash, FIDO_OK); + for (size_t i = 0; i < (dev_number - 1); i++) { + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + will_return(__wrap_fido_dev_is_fido2, true); + if (i == 0) { + will_return(__wrap_fido_dev_get_assert, FIDO_ERR_INVALID_SIG); + } else { + will_return(__wrap_fido_dev_get_assert, FIDO_OK); + } + } + will_return(__wrap_fido_dev_has_uv, false); + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_supports_uv, false); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + will_return(__wrap_fido_assert_set_clientdata_hash, FIDO_OK); + will_return(__wrap_fido_dev_has_uv, false); + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_get_assert, FIDO_OK); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + will_return(__wrap_fido_assert_verify, FIDO_OK); + + ret = authenticate(&data); + + assert_int_equal(ret, EOK); + talloc_free(tmp_ctx); +} + +void test_get_assert_data_integration(void **state) +{ + TALLOC_CTX *tmp_ctx; + struct passkey_data data; + size_t dev_number = 3; + char *key_handle; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + data.action = ACTION_GET_ASSERT; + data.domain = "test.com"; + key_handle = talloc_strdup(tmp_ctx, TEST_KEY_HANDLE); + data.key_handle_list = &key_handle; + data.key_handle_size = 1; + data.crypto_challenge = TEST_CRYPTO_CHALLENGE; + data.user_verification = FIDO_OPT_FALSE; + data.user_id = NULL; + will_return(__wrap_fido_dev_info_manifest, FIDO_OK); + will_return(__wrap_fido_dev_info_manifest, dev_number); + will_return(__wrap_fido_assert_set_rp, FIDO_OK); + will_return(__wrap_fido_assert_allow_cred, FIDO_OK); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + will_return(__wrap_fido_assert_set_clientdata_hash, FIDO_OK); + for (size_t i = 0; i < (dev_number - 1); i++) { + will_return(__wrap_fido_dev_info_path, TEST_PATH); + will_return(__wrap_fido_dev_open, FIDO_OK); + will_return(__wrap_fido_dev_is_fido2, true); + if (i == 0) { + will_return(__wrap_fido_dev_get_assert, FIDO_ERR_INVALID_SIG); + } else { + will_return(__wrap_fido_dev_get_assert, FIDO_OK); + } + } + will_return(__wrap_fido_dev_has_uv, false); + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_supports_uv, false); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + will_return(__wrap_fido_dev_has_uv, false); + will_return(__wrap_fido_dev_has_pin, false); + will_return(__wrap_fido_dev_get_assert, FIDO_OK); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + will_return(__wrap_fido_assert_authdata_ptr, TEST_HEX_AUTH_DATA); + will_return(__wrap_fido_assert_authdata_len, TEST_AUTH_DATA_LEN); + will_return(__wrap_fido_assert_sig_ptr, TEST_HEX_SIGNATURE); + will_return(__wrap_fido_assert_sig_len, TEST_SIGNATURE_LEN); + + ret = get_assert_data(&data); + + assert_int_equal(ret, EOK); + talloc_free(tmp_ctx); +} + +void test_verify_assert_data_integration(void **state) +{ + TALLOC_CTX *tmp_ctx; + struct passkey_data data; + char *key_handle; + char *public_key; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + data.action = ACTION_VERIFY_ASSERT; + data.domain = "test.com"; + key_handle = talloc_strdup(tmp_ctx, TEST_KEY_HANDLE); + public_key = talloc_strdup(tmp_ctx, TEST_ES256_PEM_PUBLIC_KEY); + data.key_handle_list = &key_handle; + data.key_handle_size = 1; + data.public_key_list = &public_key; + data.public_key_size = 1; + data.crypto_challenge = TEST_CRYPTO_CHALLENGE; + data.auth_data = TEST_B64_AUTH_DATA; + data.signature = TEST_B64_SIGNATURE; + will_return(__wrap_fido_assert_set_rp, FIDO_OK); + will_return(__wrap_fido_assert_allow_cred, FIDO_OK); + will_return(__wrap_fido_assert_set_uv, FIDO_OK); + will_return(__wrap_fido_assert_set_clientdata_hash, FIDO_OK); + will_return(__wrap_fido_assert_set_count, FIDO_OK); + will_return(__wrap_fido_assert_set_authdata, FIDO_OK); + will_return(__wrap_fido_assert_set_sig, FIDO_OK); + will_return(__wrap_fido_assert_verify, FIDO_OK); + + ret = verify_assert_data(&data); + + assert_int_equal(ret, EOK); + talloc_free(tmp_ctx); +} + +static void test_parse_supp_valgrind_args(void) +{ + /* + * The objective of this function is to filter the unit-test functions + * that trigger a valgrind memory leak and suppress them to avoid false + * positives. + */ + DEBUG_CLI_INIT(debug_level); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_parse_required_args), + cmocka_unit_test(test_parse_all_args), + cmocka_unit_test_setup_teardown(test_prepare_credentials_ok, setup, teardown), + cmocka_unit_test_setup_teardown(test_prepare_credentials_user_verification_missing, setup, teardown), + cmocka_unit_test_setup_teardown(test_prepare_credentials_error, setup, teardown), + cmocka_unit_test_setup_teardown(test_list_devices_one_device, setup, teardown), + cmocka_unit_test_setup_teardown(test_list_devices_no_device, setup, teardown), + cmocka_unit_test_setup_teardown(test_list_devices_error, setup, teardown), + cmocka_unit_test_setup_teardown(test_select_device_found, setup, teardown), + cmocka_unit_test_setup_teardown(test_select_device_open_failed, setup, teardown), + cmocka_unit_test(test_read_pass), + cmocka_unit_test_setup_teardown(test_generate_credentials_user_verification, setup, teardown), + cmocka_unit_test_setup_teardown(test_generate_credentials_pin, setup, teardown), + cmocka_unit_test_setup_teardown(test_generate_credentials_pin_error, setup, teardown), + cmocka_unit_test_setup_teardown(test_verify_credentials_basic_attestation, setup, teardown), + cmocka_unit_test_setup_teardown(test_verify_credentials_self_attestation, setup, teardown), + cmocka_unit_test_setup_teardown(test_verify_credentials_error, setup, teardown), + cmocka_unit_test(test_decode_public_key), + cmocka_unit_test(test_register_key_integration), + cmocka_unit_test(test_select_authenticator), + cmocka_unit_test_setup_teardown(test_prepare_assert_ok, setup, teardown), + cmocka_unit_test_setup_teardown(test_prepare_assert_error, setup, teardown), + cmocka_unit_test(test_reset_public_key), + cmocka_unit_test(test_encode_public_keys), + cmocka_unit_test_setup_teardown(test_get_authenticator_data_one_key, setup, teardown), + cmocka_unit_test_setup_teardown(test_get_authenticator_data_multiple_keys_assert_found, setup, teardown), + cmocka_unit_test_setup_teardown(test_get_authenticator_data_multiple_keys_assert_not_found, setup, teardown), + cmocka_unit_test_setup_teardown(test_get_device_options, setup, teardown), + cmocka_unit_test_setup_teardown(test_get_device_options_user_verification_unmatch, setup, teardown), + cmocka_unit_test_setup_teardown(test_get_device_options_user_verification_false_not_supported, setup, teardown), + cmocka_unit_test_setup_teardown(test_request_assert, setup, teardown), + cmocka_unit_test_setup_teardown(test_verify_assert, setup, teardown), + cmocka_unit_test_setup_teardown(test_verify_assert_failed, setup, teardown), + cmocka_unit_test(test_authenticate_integration), + cmocka_unit_test(test_get_assert_data_integration), + cmocka_unit_test(test_verify_assert_data_integration), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + test_parse_supp_valgrind_args(); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_prompt_config.c b/src/tests/cmocka/test_prompt_config.c new file mode 100644 index 0000000..0b761ae --- /dev/null +++ b/src/tests/cmocka/test_prompt_config.c @@ -0,0 +1,215 @@ +/* + SSSD + + prompt config - Utilities tests + + Authors: + Sumit bose <sbose@redhat.com> + + Copyright (C) 2019 Red Hat + + 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 <string.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" + +#include "sss_client/sss_cli.h" + +void test_pc_list_add_password(void **state) +{ + int ret; + struct prompt_config **pc_list = NULL; + + ret = pc_list_add_password(&pc_list, "Hello"); + assert_int_equal(ret, EOK); + assert_non_null(pc_list); + assert_non_null(pc_list[0]); + assert_int_equal(PC_TYPE_PASSWORD, pc_get_type(pc_list[0])); + assert_string_equal("Hello", pc_get_password_prompt(pc_list[0])); + assert_null(pc_list[1]); + + ret = pc_list_add_password(&pc_list, "Hello2"); + assert_int_equal(ret, EOK); + assert_non_null(pc_list); + assert_non_null(pc_list[0]); + assert_int_equal(PC_TYPE_PASSWORD, pc_get_type(pc_list[0])); + assert_string_equal("Hello", pc_get_password_prompt(pc_list[0])); + assert_non_null(pc_list[1]); + assert_int_equal(PC_TYPE_PASSWORD, pc_get_type(pc_list[1])); + assert_string_equal("Hello2", pc_get_password_prompt(pc_list[1])); + assert_null(pc_list[2]); + + pc_list_free(pc_list); +} + +void test_pc_list_add_2fa_single(void **state) +{ + int ret; + struct prompt_config **pc_list = NULL; + + ret = pc_list_add_2fa_single(&pc_list, "Hello"); + assert_int_equal(ret, EOK); + assert_non_null(pc_list); + assert_non_null(pc_list[0]); + assert_int_equal(PC_TYPE_2FA_SINGLE, pc_get_type(pc_list[0])); + assert_string_equal("Hello", pc_get_2fa_single_prompt(pc_list[0])); + assert_null(pc_list[1]); + + ret = pc_list_add_2fa_single(&pc_list, "Hello2"); + assert_int_equal(ret, EOK); + assert_non_null(pc_list); + assert_non_null(pc_list[0]); + assert_int_equal(PC_TYPE_2FA_SINGLE, pc_get_type(pc_list[0])); + assert_string_equal("Hello", pc_get_2fa_single_prompt(pc_list[0])); + assert_non_null(pc_list[1]); + assert_int_equal(PC_TYPE_2FA_SINGLE, pc_get_type(pc_list[1])); + assert_string_equal("Hello2", pc_get_2fa_single_prompt(pc_list[1])); + assert_null(pc_list[2]); + + pc_list_free(pc_list); +} + +void test_pc_list_add_2fa(void **state) +{ + int ret; + struct prompt_config **pc_list = NULL; + + ret = pc_list_add_2fa(&pc_list, "Hello", "Good Bye"); + assert_int_equal(ret, EOK); + assert_non_null(pc_list); + assert_non_null(pc_list[0]); + assert_int_equal(PC_TYPE_2FA, pc_get_type(pc_list[0])); + assert_string_equal("Hello", pc_get_2fa_1st_prompt(pc_list[0])); + assert_string_equal("Good Bye", pc_get_2fa_2nd_prompt(pc_list[0])); + assert_null(pc_list[1]); + + pc_list_free(pc_list); +} + +void test_pam_get_response_prompt_config(void **state) +{ + int ret; + struct prompt_config **pc_list = NULL; + int len; + uint8_t *data; + + ret = pc_list_add_password(&pc_list, "password"); + assert_int_equal(ret, EOK); + + ret = pc_list_add_2fa(&pc_list, "first", "second"); + assert_int_equal(ret, EOK); + + ret = pc_list_add_2fa_single(&pc_list, "single"); + assert_int_equal(ret, EOK); + + ret = pam_get_response_prompt_config(pc_list, &len, &data); + pc_list_free(pc_list); + assert_int_equal(ret, EOK); + assert_int_equal(len, 57); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + assert_memory_equal(data, "\3\0\0\0\1\0\0\0\10\0\0\0" "password\2\0\0\0\5\0\0\0" "first\6\0\0\0" "second\3\0\0\0\6\0\0\0" "single", len); +#else + assert_memory_equal(data, "\0\0\0\3\0\0\0\1\0\0\0\10" "password\0\0\0\2\0\0\0\5" "first\0\0\0\6" "second\0\0\0\3\0\0\0\6" "single", len); +#endif + + free(data); +} + +void test_pc_list_from_response(void **state) +{ + int ret; + struct prompt_config **pc_list = NULL; + int len; + uint8_t *data; + + ret = pc_list_add_password(&pc_list, "password"); + assert_int_equal(ret, EOK); + + ret = pc_list_add_2fa(&pc_list, "first", "second"); + assert_int_equal(ret, EOK); + + ret = pc_list_add_2fa_single(&pc_list, "single"); + assert_int_equal(ret, EOK); + + ret = pam_get_response_prompt_config(pc_list, &len, &data); + pc_list_free(pc_list); + assert_int_equal(ret, EOK); + assert_int_equal(len, 57); + + pc_list = NULL; + + ret = pc_list_from_response(len, data, &pc_list); + free(data); + assert_int_equal(ret, EOK); + assert_non_null(pc_list); + + assert_non_null(pc_list[0]); + assert_int_equal(PC_TYPE_PASSWORD, pc_get_type(pc_list[0])); + assert_string_equal("password", pc_get_password_prompt(pc_list[0])); + + assert_non_null(pc_list[1]); + assert_int_equal(PC_TYPE_2FA, pc_get_type(pc_list[1])); + assert_string_equal("first", pc_get_2fa_1st_prompt(pc_list[1])); + assert_string_equal("second", pc_get_2fa_2nd_prompt(pc_list[1])); + + assert_non_null(pc_list[2]); + assert_int_equal(PC_TYPE_2FA_SINGLE, pc_get_type(pc_list[2])); + assert_string_equal("single", pc_get_2fa_single_prompt(pc_list[2])); + + assert_null(pc_list[3]); + + pc_list_free(pc_list); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_pc_list_add_password), + cmocka_unit_test(test_pc_list_add_2fa_single), + cmocka_unit_test(test_pc_list_add_2fa), + cmocka_unit_test(test_pam_get_response_prompt_config), + cmocka_unit_test(test_pc_list_from_response), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_resolv_fake.c b/src/tests/cmocka/test_resolv_fake.c new file mode 100644 index 0000000..1b62f3a --- /dev/null +++ b/src/tests/cmocka/test_resolv_fake.c @@ -0,0 +1,422 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: Resolver tests using a fake resolver library + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <stdarg.h> +#include <stdlib.h> + +#include <resolv.h> + +#include "resolv/async_resolv.h" +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" + +#define TEST_BUFSIZE 1024 +#define TEST_DEFAULT_TIMEOUT 5 +#define TEST_SRV_QUERY "_ldap._tcp.sssd.com" + +static TALLOC_CTX *global_mock_context = NULL; + +struct srv_rrdata { + uint16_t port; + uint16_t prio; + uint16_t weight; + uint32_t ttl; + const char *hostname; +}; + +static ssize_t dns_header(unsigned char **buf, size_t ancount) +{ + uint8_t *hb; + HEADER h; + + hb = *buf; + memset(hb, 0, NS_HFIXEDSZ); + memset(&h, 0, sizeof(h)); + + h.id = 0xFFFF & sss_rand(); /* random query ID */ + h.qr = 1; /* response flag */ + h.rd = 1; /* recursion desired */ + h.ra = 1; /* recursion available */ + + h.qdcount = htons(1); /* no. of questions */ + h.ancount = htons(ancount); /* no. of answers */ + h.arcount = htons(0); /* no. of add'tl records */ + memcpy(hb, &h, sizeof(h)); + + hb += NS_HFIXEDSZ; /* move past the header */ + *buf = hb; + + return NS_HFIXEDSZ; +} + +static ssize_t dns_question(const char *question, + uint16_t type, + uint8_t **question_ptr, + size_t remaining) +{ + unsigned char *qb = *question_ptr; + int n; + + n = ns_name_compress(question, qb, remaining, NULL, NULL); + assert_true(n > 0); + + qb += n; + remaining -= n; + + NS_PUT16(type, qb); + NS_PUT16(ns_c_in, qb); + + *question_ptr = qb; + return n + 2 * sizeof(uint16_t); +} + +static ssize_t add_rr_common(uint16_t type, + uint32_t ttl, + size_t rdata_size, + const char *key, + size_t remaining, + uint8_t **rdata_ptr) +{ + uint8_t *rd = *rdata_ptr; + ssize_t written = 0; + + written = ns_name_compress(key, rd, remaining, NULL, NULL); + assert_int_not_equal(written, -1); + rd += written; + remaining -= written; + + assert_true(remaining > 3 * sizeof(uint16_t) + sizeof(uint32_t)); + NS_PUT16(type, rd); + NS_PUT16(ns_c_in, rd); + NS_PUT32(ttl, rd); + NS_PUT16(rdata_size, rd); + + assert_true(remaining > rdata_size); + *rdata_ptr = rd; + return written + 3 * sizeof(uint16_t) + sizeof(uint32_t) + rdata_size; +} + +static ssize_t add_srv_rr(struct srv_rrdata *rr, + const char *question, + uint8_t *answer, + size_t anslen) +{ + uint8_t *a = answer; + ssize_t resp_size; + size_t rdata_size; + unsigned char hostname_compressed[MAXDNAME]; + ssize_t compressed_len; + + rdata_size = 3 * sizeof(uint16_t); + + /* Prepare the data to write */ + compressed_len = ns_name_compress(rr->hostname, + hostname_compressed, MAXDNAME, + NULL, NULL); + assert_int_not_equal(compressed_len, -1); + rdata_size += compressed_len; + + resp_size = add_rr_common(ns_t_srv, rr->ttl, rdata_size, + question, anslen, &a); + + NS_PUT16(rr->prio, a); + NS_PUT16(rr->weight, a); + NS_PUT16(rr->port, a); + memcpy(a, hostname_compressed, compressed_len); + + return resp_size; +} + +unsigned char *create_srv_buffer(TALLOC_CTX *mem_ctx, + const char *question, + struct srv_rrdata *rrs, + size_t n_rrs, + size_t *_buflen) +{ + unsigned char *buf; + unsigned char *buf_head; + ssize_t len; + ssize_t i; + ssize_t total = 0; + + buf = talloc_zero_array(mem_ctx, unsigned char, TEST_BUFSIZE); + assert_non_null(buf); + buf_head = buf; + + len = dns_header(&buf, n_rrs); + assert_true(len > 0); + total += len; + + len = dns_question(question, ns_t_srv, &buf, TEST_BUFSIZE - total); + assert_true(len > 0); + total += len; + + /* answer */ + for (i = 0; i < n_rrs; i++) { + len = add_srv_rr(&rrs[i], question, buf, TEST_BUFSIZE - total); + assert_true(len > 0); + total += len; + buf += len; + } + + *_buflen = total; + return buf_head; +} + +struct fake_ares_query { + int status; + int timeouts; + unsigned char *abuf; + int alen; +}; + +void mock_ares_query(int status, int timeouts, unsigned char *abuf, int alen) +{ + will_return(__wrap_ares_query, status); + will_return(__wrap_ares_query, timeouts); + will_return(__wrap_ares_query, abuf); + will_return(__wrap_ares_query, alen); +} + +void __wrap_ares_query(ares_channel channel, const char *name, int dnsclass, + int type, ares_callback callback, void *arg) +{ + struct fake_ares_query query; + + query.status = sss_mock_type(int); + query.timeouts = sss_mock_type(int); + query.abuf = sss_mock_ptr_type(unsigned char *); + query.alen = sss_mock_type(int); + + callback(arg, query.status, query.timeouts, query.abuf, query.alen); +} + +/* The unit test */ +struct resolv_fake_ctx { + struct resolv_ctx *resolv; + struct sss_test_ctx *ctx; +}; + +static int test_resolv_fake_setup(void **state) +{ + struct resolv_fake_ctx *test_ctx; + int ret; + + assert_true(leak_check_setup()); + global_mock_context = talloc_new(global_talloc_context); + assert_non_null(global_mock_context); + + test_ctx = talloc_zero(global_mock_context, + struct resolv_fake_ctx); + assert_non_null(test_ctx); + + test_ctx->ctx = create_ev_test_ctx(test_ctx); + assert_non_null(test_ctx->ctx); + + ret = resolv_init(test_ctx, test_ctx->ctx->ev, + TEST_DEFAULT_TIMEOUT, 2000, true, &test_ctx->resolv); + assert_int_equal(ret, EOK); + + *state = test_ctx; + return 0; +} + +static int test_resolv_fake_teardown(void **state) +{ + struct resolv_fake_ctx *test_ctx = + talloc_get_type(*state, struct resolv_fake_ctx); + + talloc_free(test_ctx); + talloc_free(global_mock_context); + assert_true(leak_check_teardown()); + return 0; +} + +void test_resolv_fake_srv_done(struct tevent_req *req) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx; + int status; + uint32_t ttl; + struct ares_srv_reply *srv_replies = NULL; + struct resolv_fake_ctx *test_ctx = + tevent_req_callback_data(req, struct resolv_fake_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + ret = resolv_getsrv_recv(tmp_ctx, req, &status, NULL, + &srv_replies, &ttl); + assert_int_equal(ret, EOK); + + assert_non_null(srv_replies); + assert_int_equal(srv_replies->priority, 1); + assert_int_equal(srv_replies->weight, 40); + assert_int_equal(srv_replies->port, 389); + assert_string_equal(srv_replies->host, "ldap.sssd.com"); + + srv_replies = srv_replies->next; + assert_non_null(srv_replies); + assert_int_equal(srv_replies->priority, 1); + assert_int_equal(srv_replies->weight, 60); + assert_int_equal(srv_replies->port, 389); + assert_string_equal(srv_replies->host, "ldap2.sssd.com"); + + srv_replies = srv_replies->next; + assert_null(srv_replies); + + assert_int_equal(ttl, 500); + + talloc_free(tmp_ctx); + test_ev_done(test_ctx->ctx, EOK); +} + +void test_resolv_fake_srv(void **state) +{ + int ret; + struct tevent_req *req; + struct resolv_fake_ctx *test_ctx = + talloc_get_type(*state, struct resolv_fake_ctx); + + unsigned char *buf; + size_t buflen; + + struct srv_rrdata rr[2]; + + rr[0].prio = 1; + rr[0].port = 389; + rr[0].weight = 40; + rr[0].ttl = 600; + rr[0].hostname = "ldap.sssd.com"; + + rr[1].prio = 1; + rr[1].port = 389; + rr[1].weight = 60; + rr[1].ttl = 500; + rr[1].hostname = "ldap2.sssd.com"; + + buf = create_srv_buffer(test_ctx, TEST_SRV_QUERY, rr, 2, &buflen); + assert_non_null(buf); + mock_ares_query(0, 0, buf, buflen); + + req = resolv_getsrv_send(test_ctx, test_ctx->ctx->ev, + test_ctx->resolv, TEST_SRV_QUERY); + assert_non_null(req); + tevent_req_set_callback(req, test_resolv_fake_srv_done, test_ctx); + + ret = test_ev_loop(test_ctx->ctx); + assert_int_equal(ret, ERR_OK); +} + +void test_resolv_is_address(void **state) +{ + bool ret; + + ret = resolv_is_address("10.192.211.37"); + assert_true(ret); + + ret = resolv_is_address("127.0.0.1"); + assert_true(ret); + + ret = resolv_is_address("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + assert_true(ret); + + ret = resolv_is_address("sssd.ldap.com"); + assert_false(ret); + + ret = resolv_is_address("testhostname"); + assert_false(ret); + + ret = resolv_is_address("localhost"); + assert_false(ret); +} + +void test_resolv_is_unix(void **state) +{ + bool ret; + + ret = resolv_is_unix("10.192.211.37"); + assert_false(ret); + + ret = resolv_is_unix("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + assert_false(ret); + + ret = resolv_is_unix("sssd.ldap.com"); + assert_false(ret); + + ret = resolv_is_unix("testhostname"); + assert_false(ret); + + ret = resolv_is_unix("/tmp/socket"); + assert_true(ret); +} + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_resolv_fake_srv, + test_resolv_fake_setup, + test_resolv_fake_teardown), + cmocka_unit_test(test_resolv_is_address), + cmocka_unit_test(test_resolv_is_unix), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + return rv; +} diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c new file mode 100644 index 0000000..fe69a9d --- /dev/null +++ b/src/tests/cmocka/test_responder_cache_req.c @@ -0,0 +1,4585 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 <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 "db/sysdb.h" +#include "responder/common/cache_req/cache_req.h" + +#ifdef BUILD_FILES_PROVIDER +#define FILES_ID_PROVIDER "files" +#endif +#define LDAP_ID_PROVIDER "ldap" +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_responder_cache_req_conf.ldb" +#define TEST_DOM_NAME "responder_cache_req_test" + +#define TEST_USER_PREFIX "test*" +#define TEST_NO_USER_PREFIX "nosuchuser*" +#define TEST_GROUP_PREFIX "test-group*" +#define TEST_NO_GROUP_PREFIX "nosuchgroup*" + +struct test_user { + const char *short_name; + const char *upn; + const char *sid; + uid_t uid; + gid_t gid; +} users[] = {{"test-user1", "upn1@upndomain.com", + "S-1-5-21-3623811015-3361044348-30300820-1001", 3001, 3001}, + {"test-user2", "upn2@upndomain.com", + "S-1-5-21-3623811015-3361044348-30300820-1002", 3002, 3002}}; + +struct test_group { + const char *short_name; + const char *sid; + gid_t gid; +} groups[] = {{"test-group1", "S-1-5-21-3623811015-3361044348-30300820-2001", 4001}, + {"test-group2", "S-1-5-21-3623811015-3361044348-30300820-2002", 4002}}; + +#define new_single_domain_test(test) \ + cmocka_unit_test_setup_teardown(test_ ## test, \ + test_single_domain_setup, \ + test_single_domain_teardown) + +#ifdef BUILD_FILES_PROVIDER +#define new_files_domain_test(test) \ + cmocka_unit_test_setup_teardown(test_ ## test, \ + test_files_domain_setup, \ + test_single_domain_teardown) +#endif + +#define new_single_domain_id_limit_test(test) \ + cmocka_unit_test_setup_teardown(test_ ## test, \ + test_single_domain_id_limits_setup, \ + test_single_domain_teardown) + +#define new_multi_domain_test(test) \ + cmocka_unit_test_setup_teardown(test_ ## test, \ + test_multi_domain_setup, \ + test_multi_domain_teardown) + +#define new_subdomain_test(test) \ + cmocka_unit_test_setup_teardown(test_ ## test, \ + test_subdomain_setup, \ + test_subdomain_teardown) + +#define run_cache_req(ctx, send_fn, done_fn, dom, crp, lookup, expret) do { \ + TALLOC_CTX *req_mem_ctx; \ + struct tevent_req *req; \ + errno_t ret; \ + \ + req_mem_ctx = talloc_new(global_talloc_context); \ + check_leaks_push(req_mem_ctx); \ + \ + req = send_fn(req_mem_ctx, ctx->tctx->ev, ctx->rctx, \ + ctx->ncache, crp, \ + (dom == NULL ? NULL : dom->name), lookup); \ + assert_non_null(req); \ + tevent_req_set_callback(req, done_fn, ctx); \ + \ + ret = test_ev_loop(ctx->tctx); \ + assert_int_equal(ret, expret); \ + assert_true(check_leaks_pop(req_mem_ctx)); \ + \ + talloc_free(req_mem_ctx); \ +} while (0) + +#define run_cache_req_domtype(ctx, send_fn, done_fn, dom, crp, domtype, lookup, expret) do { \ + TALLOC_CTX *req_mem_ctx; \ + struct tevent_req *req; \ + errno_t ret; \ + \ + req_mem_ctx = talloc_new(global_talloc_context); \ + check_leaks_push(req_mem_ctx); \ + \ + req = send_fn(req_mem_ctx, ctx->tctx->ev, ctx->rctx, \ + ctx->ncache, crp, \ + domtype, \ + (dom == NULL ? NULL : dom->name), lookup); \ + assert_non_null(req); \ + tevent_req_set_callback(req, done_fn, ctx); \ + \ + ret = test_ev_loop(ctx->tctx); \ + assert_int_equal(ret, expret); \ + assert_true(check_leaks_pop(req_mem_ctx)); \ + \ + talloc_free(req_mem_ctx); \ +} while (0) + +struct cache_req_test_ctx { + struct sss_test_ctx *tctx; + struct resp_ctx *rctx; + struct sss_nc_ctx *ncache; + struct sss_domain_info *subdomain; + + struct cache_req_result *result; + bool dp_called; + + /* NOTE: Please, instead of adding new create_[user|group] bool, + * use bitshift. */ + bool create_user1; + bool create_user2; + bool create_group1; + bool create_group2; + bool create_subgroup1; + bool create_subuser1; +}; + +const char *domains[] = {"responder_cache_req_test_a", + "responder_cache_req_test_b", + "responder_cache_req_test_c", + "responder_cache_req_test_d", + NULL}; + +const char *subdomain_name = "responder_cache_req_test_a_sub"; + +struct cli_protocol_version *register_cli_protocol_version(void) +{ + static struct cli_protocol_version version[] = { + { 0, NULL, NULL } + }; + + return version; +} + +static void cache_req_user_by_name_test_done(struct tevent_req *req) +{ + struct cache_req_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct cache_req_test_ctx); + + ctx->tctx->error = cache_req_user_by_name_recv(ctx, req, &ctx->result); + talloc_zfree(req); + + ctx->tctx->done = true; +} + +static void cache_req_user_by_id_test_done(struct tevent_req *req) +{ + struct cache_req_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct cache_req_test_ctx); + + ctx->tctx->error = cache_req_user_by_id_recv(ctx, req, &ctx->result); + talloc_zfree(req); + + ctx->tctx->done = true; +} + +static void cache_req_group_by_name_test_done(struct tevent_req *req) +{ + struct cache_req_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct cache_req_test_ctx); + + ctx->tctx->error = cache_req_group_by_name_recv(ctx, req, &ctx->result); + talloc_zfree(req); + + ctx->tctx->done = true; +} + +static void cache_req_group_by_id_test_done(struct tevent_req *req) +{ + struct cache_req_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct cache_req_test_ctx); + + ctx->tctx->error = cache_req_group_by_id_recv(ctx, req, &ctx->result); + talloc_zfree(req); + + ctx->tctx->done = true; +} + +static void cache_req_object_by_sid_test_done(struct tevent_req *req) +{ + struct cache_req_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct cache_req_test_ctx); + + ctx->tctx->error = cache_req_object_by_sid_recv(ctx, req, &ctx->result); + talloc_zfree(req); + + ctx->tctx->done = true; +} + +static void cache_req_object_by_id_test_done(struct tevent_req *req) +{ + struct cache_req_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct cache_req_test_ctx); + + ctx->tctx->error = cache_req_object_by_id_recv(ctx, req, &ctx->result); + talloc_zfree(req); + + ctx->tctx->done = true; +} + +static void prepare_user(struct sss_domain_info *domain, + struct test_user *user, + uint64_t timeout, + time_t transaction_time) +{ + struct sysdb_attrs *attrs; + errno_t ret; + char *fqname; + + attrs = sysdb_new_attrs(NULL); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, user->upn); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, user->sid); + assert_int_equal(ret, EOK); + + fqname = sss_create_internal_fqname(attrs, user->short_name, domain->name); + assert_non_null(fqname); + + ret = sysdb_store_user(domain, fqname, "pwd", + user->uid, user->gid, NULL, NULL, NULL, + "cn=origdn,dc=test", attrs, NULL, + timeout, transaction_time); + talloc_free(fqname); + assert_int_equal(ret, EOK); + + talloc_free(attrs); +} + +static void run_user_by_name(struct cache_req_test_ctx *test_ctx, + struct sss_domain_info *domain, + int cache_refresh_percent, + errno_t exp_ret) +{ + run_cache_req_domtype(test_ctx, cache_req_user_by_name_send, + cache_req_user_by_name_test_done, domain, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, + users[0].short_name, exp_ret); +} + +static void run_user_by_upn(struct cache_req_test_ctx *test_ctx, + struct sss_domain_info *domain, + int cache_refresh_percent, + errno_t exp_ret) +{ + run_cache_req_domtype(test_ctx, cache_req_user_by_name_send, + cache_req_user_by_name_test_done, domain, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, + users[0].upn, exp_ret); +} + +static void run_user_by_id(struct cache_req_test_ctx *test_ctx, + struct sss_domain_info *domain, + int cache_refresh_percent, + errno_t exp_ret) +{ + run_cache_req(test_ctx, cache_req_user_by_id_send, + cache_req_user_by_id_test_done, domain, + cache_refresh_percent, users[0].uid, exp_ret); +} + +static void +run_user_by_name_with_requested_domains(struct cache_req_test_ctx *test_ctx, + struct sss_domain_info *domain, + char **requested_domains, + int cache_refresh_percent, + errno_t exp_ret) +{ + TALLOC_CTX *req_mem_ctx; + struct tevent_req *req; + errno_t ret; + struct cache_req_data *data; + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + data = cache_req_data_name(req_mem_ctx, CACHE_REQ_USER_BY_NAME, + users[0].short_name); + assert_non_null(data); + + cache_req_data_set_requested_domains(data, requested_domains); + + req = cache_req_send(req_mem_ctx, test_ctx->tctx->ev, test_ctx->rctx, + test_ctx->ncache, cache_refresh_percent, + CACHE_REQ_POSIX_DOM, + (domain == NULL ? NULL : domain->name), data); + assert_non_null(req); + talloc_steal(req, data); + + assert_non_null(req); + tevent_req_set_callback(req, cache_req_user_by_name_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, exp_ret); + assert_true(check_leaks_pop(req_mem_ctx)); + + talloc_free(req_mem_ctx); +} + +static void assert_msg_has_shortname(struct cache_req_test_ctx *test_ctx, + struct ldb_message *msg, + const char *check_name) +{ + const char *ldbname; + char *shortname; + errno_t ret; + + ldbname = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + assert_non_null(ldbname); + ret = sss_parse_internal_fqname(test_ctx, ldbname, &shortname, NULL); + assert_int_equal(ret, EOK); + assert_string_equal(shortname, check_name); + talloc_free(shortname); +} + +static void check_user(struct cache_req_test_ctx *test_ctx, + struct test_user *user, + struct sss_domain_info *exp_dom) +{ + const char *ldbupn; + const char *ldbsid; + uid_t ldbuid; + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, 1); + assert_non_null(test_ctx->result->msgs); + assert_non_null(test_ctx->result->msgs[0]); + + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[0], + user->short_name); + + ldbupn = ldb_msg_find_attr_as_string(test_ctx->result->msgs[0], + SYSDB_UPN, NULL); + assert_non_null(ldbupn); + assert_string_equal(ldbupn, user->upn); + + ldbsid = ldb_msg_find_attr_as_string(test_ctx->result->msgs[0], + SYSDB_SID_STR, NULL); + assert_non_null(ldbsid); + assert_string_equal(ldbsid, user->sid); + + ldbuid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0], + SYSDB_UIDNUM, 0); + assert_int_equal(ldbuid, user->uid); + + assert_non_null(test_ctx->result->domain); + assert_string_equal(exp_dom->name, test_ctx->result->domain->name); +} + +static void prepare_group(struct sss_domain_info *domain, + struct test_group *group, + uint64_t timeout, + time_t transaction_time) +{ + struct sysdb_attrs *attrs; + char *fqname; + errno_t ret; + + attrs = sysdb_new_attrs(NULL); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, group->sid); + assert_int_equal(ret, EOK); + + fqname = sss_create_internal_fqname(attrs, group->short_name, domain->name); + assert_non_null(fqname); + + ret = sysdb_store_group(domain, fqname, group->gid, attrs, + timeout, transaction_time); + talloc_free(fqname); + assert_int_equal(ret, EOK); + + talloc_free(attrs); +} + +static void run_group_by_name(struct cache_req_test_ctx *test_ctx, + struct sss_domain_info *domain, + int cache_refresh_percent, + errno_t exp_ret) +{ + run_cache_req_domtype(test_ctx, cache_req_group_by_name_send, + cache_req_group_by_name_test_done, domain, + cache_refresh_percent, + CACHE_REQ_POSIX_DOM, + groups[0].short_name, exp_ret); +} + +static void run_group_by_id(struct cache_req_test_ctx *test_ctx, + struct sss_domain_info *domain, + int cache_refresh_percent, + errno_t exp_ret) +{ + run_cache_req(test_ctx, cache_req_group_by_id_send, + cache_req_group_by_id_test_done, domain, + cache_refresh_percent, groups[0].gid, exp_ret); +} + +static void check_group(struct cache_req_test_ctx *test_ctx, + struct test_group *group, + struct sss_domain_info *exp_dom) +{ + const char *ldbsid; + gid_t ldbgid; + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, 1); + assert_non_null(test_ctx->result->msgs); + assert_non_null(test_ctx->result->msgs[0]); + + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[0], + group->short_name); + + ldbsid = ldb_msg_find_attr_as_string(test_ctx->result->msgs[0], + SYSDB_SID_STR, NULL); + assert_non_null(ldbsid); + assert_string_equal(ldbsid, group->sid); + + ldbgid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0], + SYSDB_GIDNUM, 0); + assert_int_equal(ldbgid, group->gid); + + assert_non_null(test_ctx->result->domain); + assert_string_equal(exp_dom->name, test_ctx->result->domain->name); +} + +static void run_object_by_sid(struct cache_req_test_ctx *test_ctx, + struct sss_domain_info *domain, + const char *sid, + const char **attrs, + int cache_refresh_percent, + errno_t exp_ret) +{ + TALLOC_CTX *req_mem_ctx; + struct tevent_req *req; + errno_t ret; + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + req = cache_req_object_by_sid_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, test_ctx->ncache, cache_refresh_percent, + (domain == NULL ? NULL : domain->name), sid, attrs); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_object_by_sid_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, exp_ret); + assert_true(check_leaks_pop(req_mem_ctx)); + + talloc_free(req_mem_ctx); +} + +static void run_object_by_id(struct cache_req_test_ctx *test_ctx, + struct sss_domain_info *domain, + id_t id, + const char **attrs, + int cache_refresh_percent, + errno_t exp_ret) +{ + TALLOC_CTX *req_mem_ctx; + struct tevent_req *req; + errno_t ret; + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + req = cache_req_object_by_id_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, test_ctx->ncache, cache_refresh_percent, + (domain == NULL ? NULL : domain->name), id, attrs); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_object_by_id_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, exp_ret); + assert_true(check_leaks_pop(req_mem_ctx)); + + talloc_free(req_mem_ctx); +} + +struct tevent_req * +__wrap_sss_dp_get_account_send(TALLOC_CTX *mem_ctx, + struct resp_ctx *rctx, + struct sss_domain_info *dom, + bool fast_reply, + enum sss_dp_acct_type type, + const char *opt_name, + uint32_t opt_id, + const char *extra) +{ + struct cache_req_test_ctx *ctx = NULL; + + ctx = sss_mock_ptr_type(struct cache_req_test_ctx*); + ctx->dp_called = true; + + if (ctx->create_user1) { + prepare_user(ctx->tctx->dom, &users[0], 1000, time(NULL)); + } + + if (ctx->create_user2) { + prepare_user(ctx->tctx->dom, &users[1], 1000, time(NULL)); + } + + if (ctx->create_group1) { + prepare_group(ctx->tctx->dom, &groups[0], 1000, time(NULL)); + } + + if (ctx->create_group2) { + prepare_group(ctx->tctx->dom, &groups[1], 1000, time(NULL)); + } + + if (ctx->create_subgroup1) { + struct sss_domain_info *domain = NULL; + + domain = find_domain_by_name(ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 1000, time(NULL)); + } + + if (ctx->create_subuser1) { + struct sss_domain_info *domain = NULL; + + domain = find_domain_by_name(ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_user(domain, &users[0], 1000, time(NULL)); + } + + return test_req_succeed_send(mem_ctx, rctx->ev); +} + +static int test_single_domain_setup_common(void **state, + struct sss_test_conf_param *params, + const char *id_provider) +{ + struct cache_req_test_ctx *test_ctx = NULL; + errno_t ret; + + assert_true(leak_check_setup()); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx = talloc_zero(global_talloc_context, struct cache_req_test_ctx); + assert_non_null(test_ctx); + *state = test_ctx; + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, id_provider, params); + assert_non_null(test_ctx->tctx); + + test_ctx->rctx = mock_rctx(test_ctx, test_ctx->tctx->ev, + test_ctx->tctx->dom, NULL); + assert_non_null(test_ctx->rctx); + + ret = sss_ncache_init(test_ctx, 10, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + check_leaks_push(test_ctx); + + return 0; +} + +#ifdef BUILD_FILES_PROVIDER +int test_files_domain_setup(void **state) +{ + return test_single_domain_setup_common(state, NULL, FILES_ID_PROVIDER); +} +#endif + +int test_single_domain_setup(void **state) +{ + return test_single_domain_setup_common(state, NULL, LDAP_ID_PROVIDER); +} + +int test_single_domain_teardown(void **state) +{ + struct cache_req_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + talloc_zfree(test_ctx->result); + + assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + assert_true(leak_check_teardown()); + return 0; +} + +int test_single_domain_id_limits_setup(void **state) +{ + struct sss_test_conf_param params[] = { + { "min_id", "100" }, + { "max_id", "10000" }, + { NULL, NULL }, /* Sentinel */ + }; + return test_single_domain_setup_common(state, params, LDAP_ID_PROVIDER); +} + +static int test_multi_domain_setup(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + errno_t ret; + + assert_true(leak_check_setup()); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx = talloc_zero(global_talloc_context, struct cache_req_test_ctx); + assert_non_null(test_ctx); + *state = test_ctx; + + test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains, + LDAP_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + test_ctx->rctx = mock_rctx(test_ctx, test_ctx->tctx->ev, + test_ctx->tctx->dom, NULL); + assert_non_null(test_ctx->rctx); + + ret = sss_ncache_init(test_ctx, 10, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + reset_ldb_errstrings(test_ctx->tctx->dom); + check_leaks_push(test_ctx); + + return 0; +} + +void test_user_by_id_below_id_range(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Test. */ + run_cache_req(test_ctx, cache_req_user_by_id_send, + cache_req_user_by_id_test_done, test_ctx->tctx->dom, + 0, 10, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_user_by_id_above_id_range(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Test. */ + run_cache_req(test_ctx, cache_req_user_by_id_send, + cache_req_user_by_id_test_done, test_ctx->tctx->dom, + 0, 100000, ENOENT); + assert_false(test_ctx->dp_called); +} + +static int test_multi_domain_teardown(void **state) +{ + struct cache_req_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + talloc_zfree(test_ctx->result); + + reset_ldb_errstrings(test_ctx->tctx->dom); + assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); + assert_true(leak_check_teardown()); + return 0; +} + +int test_subdomain_setup(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + int ret; + const char *const testdom[4] = { subdomain_name, "TEST_A.SUB", "test_a", "S-3" }; + + assert_true(leak_check_setup()); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx = talloc_zero(global_talloc_context, struct cache_req_test_ctx); + assert_non_null(test_ctx); + *state = test_ctx; + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, LDAP_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + test_ctx->rctx = mock_rctx(test_ctx, test_ctx->tctx->ev, + test_ctx->tctx->dom, NULL); + assert_non_null(test_ctx->rctx); + + ret = sss_ncache_init(test_ctx, 10, 0, &test_ctx->ncache); + assert_int_equal(ret, EOK); + + test_ctx->subdomain = new_subdomain(test_ctx, test_ctx->tctx->dom, + testdom[0], testdom[1], testdom[2], testdom[0], + testdom[3], MPG_DISABLED, false, NULL, NULL, 0, + test_ctx->tctx->confdb, true); + assert_non_null(test_ctx->subdomain); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + testdom[0], testdom[1], testdom[2], testdom[0], + testdom[3], MPG_DISABLED, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, + test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + *state = test_ctx; + check_leaks_push(test_ctx); + return 0; +} + +int test_subdomain_teardown(void **state) +{ + struct cache_req_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + talloc_zfree(test_ctx->result); + talloc_zfree(test_ctx->rctx->cr_domains); + + assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + assert_true(leak_check_teardown()); + return 0; +} + +void test_user_by_name_multiple_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_user(domain, &users[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_user_by_name(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); +} + +void test_user_by_name_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_user_by_name(test_ctx, NULL, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_user_by_name_multiple_domains_parse(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + char *input_fqn; + char *fqname; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Add user to the first domain with different uid then test user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_a", true); + assert_non_null(domain); + + fqname = sss_create_internal_fqname(test_ctx, users[0].short_name, domain->name); + assert_non_null(fqname); + + ret = sysdb_store_user(domain, fqname, "pwd", 2000, 1000, + NULL, NULL, NULL, "cn=test-user,dc=test", NULL, + NULL, 1000, time(NULL)); + talloc_zfree(fqname); + assert_int_equal(ret, EOK); + + /* Add test user to the last domain. */ + + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_user(domain, &users[0], 1000, time(NULL)); + + /* Append domain name to the username to form the qualified input. + * We don't use the internal fqname here on purpose, because this is + * the user's input. + */ + input_fqn = talloc_asprintf(test_ctx, "%s@%s", users[0].short_name, + "responder_cache_req_test_d"); + assert_non_null(input_fqn); + + /* Mock values. */ + mock_parse_inp(users[0].short_name, "responder_cache_req_test_d", ERR_OK); + + /* Test. */ + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + req = cache_req_user_by_name_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, test_ctx->ncache, + CACHE_REQ_POSIX_DOM, + 0, + NULL, input_fqn); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_user_by_name_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + assert_false(test_ctx->dp_called); + + check_user(test_ctx, &users[0], domain); + + assert_non_null(test_ctx->result->lookup_name); + assert_string_equal(input_fqn, test_ctx->result->lookup_name); + + talloc_free(input_fqn); +} + +void test_user_by_name_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 1000, time(NULL)); + + /* Mock values */ + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_user_by_name(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_name_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], -1000, time(NULL)); + + /* Mock values */ + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Mock values. */ + /* DP should be contacted */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_user_by_name(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_name_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 50, time(NULL) - 26); + + /* Mock values. */ + /* DP should be contacted without callback */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_user_by_name(test_ctx, test_ctx->tctx->dom, 50, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_name_ncache(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + errno_t ret; + char *fqname; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + fqname = sss_create_internal_fqname(test_ctx, users[0].short_name, + test_ctx->tctx->dom->name); + assert_non_null(fqname); + + ret = sss_ncache_set_user(test_ctx->ncache, false, + test_ctx->tctx->dom, fqname); + talloc_free(fqname); + assert_int_equal(ret, EOK); + + /* Mock values */ + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_user_by_name(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_user_by_name_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + test_ctx->create_user1 = true; + test_ctx->create_user2 = false; + + /* Test. */ + run_user_by_name(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_name_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_user_by_name(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_user_by_name_missing_notfound_cache_first(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->rctx->cache_first = true; + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_user_by_name(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_user_by_name_missing_notfound_full_name(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(users[0].short_name, TEST_DOM_NAME, ERR_OK); + + /* Test. */ + run_user_by_name(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_user_by_name_missing_notfound_cache_first_full_name(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->rctx->cache_first = true; + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(users[0].short_name, TEST_DOM_NAME, ERR_OK); + + /* Test. */ + run_user_by_name(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_user_by_name_multiple_domains_requested_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + char *requested_domains[2] = { discard_const("responder_cache_req_test_d"), + NULL}; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_user(domain, &users[0], 1000, time(NULL)); + + /* Mock values. */ + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_user_by_name_with_requested_domains(test_ctx, NULL, requested_domains, + 0, ERR_OK); + /* The backend will not be called during this test because the user is + * already cached in the requested domain. */ + check_user(test_ctx, &users[0], domain); +} + +void test_user_by_name_multiple_domains_requested_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + char *requested_domains[2] = { discard_const("responder_cache_req_test_a"), + NULL}; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_user(domain, &users[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + mock_parse_inp(users[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_user_by_name_with_requested_domains(test_ctx, NULL, requested_domains, + 0, ENOENT); + /* The requested domain is not the domain the user was added to, so we + * expect ENOENT and that the backend is called. */ + assert_true(test_ctx->dp_called); +} + + +void test_user_by_upn_multiple_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_user(domain, &users[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + /* Test. */ + run_user_by_upn(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); +} + +void test_user_by_upn_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + /* Test. */ + run_user_by_upn(test_ctx, NULL, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_user_by_upn_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 1000, time(NULL)); + + /* Mock values. */ + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + /* Test. */ + run_user_by_upn(test_ctx, NULL, 0, ERR_OK); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_upn_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], -1000, time(NULL)); + + /* Mock values. */ + /* DP should be contacted */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + /* Test. */ + run_user_by_upn(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_upn_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 50, time(NULL) - 26); + + /* Mock values. */ + /* DP should be contacted without callback */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + /* Test. */ + run_user_by_upn(test_ctx, NULL, 50, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_upn_ncache(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user's UPN. */ + ret = sss_ncache_set_upn(test_ctx->ncache, false, + test_ctx->tctx->dom, users[0].upn); + assert_int_equal(ret, EOK); + + /* Mock values. */ + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + /* Test. */ + run_user_by_upn(test_ctx, NULL, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_user_by_upn_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + test_ctx->create_user1 = true; + test_ctx->create_user2 = false; + + /* Test. */ + run_user_by_upn(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_upn_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(NULL, NULL, ERR_DOMAIN_NOT_FOUND); + + /* Test. */ + run_user_by_upn(test_ctx, NULL, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_user_by_id_multiple_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_user(domain, &users[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); +} + +void test_user_by_id_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_user_by_id_multiple_domains_locator_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d"); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_user(domain, &users[0], 1000, time(NULL)); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, EOK); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ERR_OK); + /* Even though the locator tells us to skip all domains except d, the domains + * are standalone and the result of the locator request is only valid within + * the subdomains + */ + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_user_by_id_multiple_domains_locator_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d"); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_user(domain, &users[0], -1000, time(NULL)); + + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, EOK); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_user_by_id_sub_domains_locator_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_user(domain, &users[0], 1000, time(NULL)); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ERR_OK); + + /* Even though the ID is present in the last domain, + * we're not calling sss_dp_get_account_send, + * because the locator will cause cache_req to skip + * all domains except _d + */ + assert_false(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_user_by_id_sub_domains_locator_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_user(domain, &users[0], -1000, time(NULL)); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_user_by_id_sub_domains_locator_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_user(domain, &users[0], 50, time(NULL) - 26); + + /* Note - DP will only be called once and we're not waiting + * for the results (so, we're not mocking _recv) + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 50, ERR_OK); + + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_user_by_id_sub_domains_locator_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + test_ctx->create_subuser1 = true; + run_user_by_id(test_ctx, NULL, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_group_by_id_below_id_range(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Test. */ + run_cache_req(test_ctx, cache_req_group_by_id_send, + cache_req_group_by_id_test_done, test_ctx->tctx->dom, + 0, 10, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_group_by_id_above_id_range(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Test. */ + run_cache_req(test_ctx, cache_req_group_by_id_send, + cache_req_group_by_id_test_done, test_ctx->tctx->dom, + 0, 100000, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_user_by_id_sub_domains_locator_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + will_return(sss_dp_get_account_domain_recv, ERR_NOT_FOUND); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_user_by_id_sub_domains_locator_cache_expired_two_calls(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + test_ctx->create_subuser1 = true; + prepare_user(domain, &users[0], -1000, time(NULL)); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_user_by_id(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + /* Request the same user again */ + test_ctx->tctx->done = false; + talloc_zfree(test_ctx->result); + + run_user_by_id(test_ctx, NULL, 0, ERR_OK); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_user_by_id_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 1000, time(NULL)); + + /* Test. */ + run_user_by_id(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_id_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], -1000, time(NULL)); + + /* Mock values. */ + /* DP should be contacted. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_user_by_id(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_id_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 50, time(NULL) - 26); + + /* Mock values. */ + /* DP should be contacted without callback */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + /* Test. */ + run_user_by_id(test_ctx, test_ctx->tctx->dom, 50, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_id_ncache(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + ret = sss_ncache_set_uid(test_ctx->ncache, false, NULL, users[0].uid); + assert_int_equal(ret, EOK); + + /* Test. */ + run_user_by_id(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_user_by_id_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + test_ctx->create_user1 = true; + test_ctx->create_user2 = false; + + /* Test. */ + run_user_by_id(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_user_by_id_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_user_by_id(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_group_by_name_multiple_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_group_by_name(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); +} + +void test_group_by_name_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_group_by_name(test_ctx, NULL, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_group_by_name_multiple_domains_parse(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + char *input_fqn; + char *fqname; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Add group to the first domain. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_a", true); + assert_non_null(domain); + + fqname = sss_create_internal_fqname(test_ctx, users[0].short_name, domain->name); + assert_non_null(fqname); + + ret = sysdb_store_group(domain, fqname, 2000, NULL, + 1000, time(NULL)); + talloc_zfree(fqname); + assert_int_equal(ret, EOK); + + /* Add group to the last domain, with different gid. */ + + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_group(domain, &groups[0], 1000, time(NULL)); + + /* Append domain name to the groupname. + * We don't use the internal fqname here on purpose, because this is + * the user's input. + */ + input_fqn = talloc_asprintf(test_ctx, "%s@%s", groups[0].short_name, + "responder_cache_req_test_d"); + assert_non_null(input_fqn); + + /* Test. */ + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + mock_parse_inp(groups[0].short_name, "responder_cache_req_test_d", ERR_OK); + + req = cache_req_group_by_name_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, test_ctx->ncache, 0, + CACHE_REQ_POSIX_DOM, NULL, + input_fqn); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_group_by_name_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + assert_false(test_ctx->dp_called); + + check_group(test_ctx, &groups[0], domain); + + assert_non_null(test_ctx->result->lookup_name); + assert_string_equal(input_fqn, test_ctx->result->lookup_name); + + talloc_free(input_fqn); +} + +void test_group_by_name_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + prepare_group(test_ctx->tctx->dom, &groups[0], 1000, time(NULL)); + + /* Mock values */ + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_group_by_name(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_group_by_name_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + prepare_group(test_ctx->tctx->dom, &groups[0], -1000, time(NULL)); + + /* Mock values. */ + /* DP should be contacted */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_group_by_name(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_group_by_name_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + prepare_group(test_ctx->tctx->dom, &groups[0], 50, time(NULL) - 26); + + /* Mock values. */ + /* DP should be contacted without callback */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_group_by_name(test_ctx, test_ctx->tctx->dom, 50, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_group_by_name_ncache(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + errno_t ret; + char *fqname; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + fqname = sss_create_internal_fqname(test_ctx, groups[0].short_name, + test_ctx->tctx->dom->name); + assert_non_null(fqname); + + ret = sss_ncache_set_group(test_ctx->ncache, false, + test_ctx->tctx->dom, fqname); + talloc_free(fqname); + assert_int_equal(ret, EOK); + + /* Mock values */ + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_group_by_name(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_group_by_name_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + test_ctx->create_group1 = true; + test_ctx->create_group2 = false; + + /* Test. */ + run_group_by_name(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_group_by_name_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(groups[0].short_name, NULL, ERR_OK); + + /* Test. */ + run_group_by_name(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_group_by_id_multiple_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); +} + +void test_group_by_id_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_group_by_id_multiple_domains_outside_id_range(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + struct sss_domain_info *domain_a = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + domain_a = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_a", true); + assert_non_null(domain_a); + domain_a->id_min = 1; + domain_a->id_max = 100; + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); +} + +void test_group_by_id_multiple_domains_locator_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d"); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 1000, time(NULL)); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, EOK); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ERR_OK); + + /* Even though the locator tells us to skip all domains except d, the domains + * are standalone and the result of the locator request is only valid within + * the subdomains + */ + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_group_by_id_multiple_domains_locator_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d"); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_group(domain, &groups[0], -1000, time(NULL)); + + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, EOK); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_group_by_id_sub_domains_locator_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 1000, time(NULL)); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ERR_OK); + + /* Even though the ID is present in the last domain, + * we're not calling sss_dp_get_account_send, + * because the locator will cause cache_req to skip + * all domains except _d + */ + assert_false(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_group_by_id_sub_domains_locator_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_group(domain, &groups[0], -1000, time(NULL)); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_group_by_id_sub_domains_locator_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 50, time(NULL) - 26); + + /* Note - DP will only be called once and we're not waiting + * for the results (so, we're not mocking _recv) + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 50, ERR_OK); + + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_group_by_id_sub_domains_locator_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + test_ctx->create_subgroup1 = true; + run_group_by_id(test_ctx, NULL, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_group_by_id_sub_domains_locator_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + will_return(sss_dp_get_account_domain_recv, ERR_NOT_FOUND); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_group_by_id_sub_domains_locator_cache_expired_two_calls(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + test_ctx->create_subgroup1 = true; + prepare_group(domain, &groups[0], -1000, time(NULL)); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_group_by_id(test_ctx, NULL, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + /* Request the same group again */ + test_ctx->tctx->done = false; + talloc_zfree(test_ctx->result); + + run_group_by_id(test_ctx, NULL, 0, ERR_OK); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_group_by_id_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + prepare_group(test_ctx->tctx->dom, &groups[0], 1000, time(NULL)); + + /* Test. */ + run_group_by_id(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_group_by_id_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + prepare_group(test_ctx->tctx->dom, &groups[0], -1000, time(NULL)); + + /* Mock values. */ + /* DP should be contacted */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_group_by_id(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_group_by_id_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + prepare_group(test_ctx->tctx->dom, &groups[0], 50, time(NULL) - 26); + + /* Mock values. */ + /* DP should be contacted without callback */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + /* Test. */ + run_group_by_id(test_ctx, test_ctx->tctx->dom, 50, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_group_by_id_ncache(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. */ + ret = sss_ncache_set_gid(test_ctx->ncache, false, NULL, groups[0].gid); + assert_int_equal(ret, EOK); + + /* Test. */ + run_group_by_id(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_group_by_id_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + test_ctx->create_group1 = true; + test_ctx->create_group2 = false; + + /* Test. */ + run_group_by_id(test_ctx, test_ctx->tctx->dom, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_group_by_id_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_group_by_id(test_ctx, test_ctx->tctx->dom, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +static void cache_req_user_by_filter_test_done(struct tevent_req *req) +{ + struct cache_req_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct cache_req_test_ctx); + + ctx->tctx->error = cache_req_user_by_filter_recv(ctx, req, &ctx->result); + talloc_zfree(req); + ctx->tctx->done = true; +} + +void test_user_by_recent_filter_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->create_user1 = true; + test_ctx->create_user2 = false; + + prepare_user(test_ctx->tctx->dom, &users[1], 1000, time(NULL) - 1); + + req_mem_ctx = talloc_new(test_ctx->tctx); + check_leaks_push(req_mem_ctx); + + /* Mock values */ + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_USER_PREFIX, NULL, ERR_OK); + + /* User TEST_USER is created with a DP callback. */ + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + NULL, + TEST_USER_PREFIX); + assert_non_null(req); + + tevent_req_set_callback(req, cache_req_user_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, 1); + + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[0], + users[0].short_name); +} + +void test_users_by_recent_filter_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + size_t num_users = 2; + const char **user_names; + const char *ldb_results[num_users]; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->create_user1 = true; + test_ctx->create_user2 = true; + + req_mem_ctx = talloc_new(test_ctx->tctx); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_USER_PREFIX, NULL, ERR_OK); + + /* User TEST_USER1 and TEST_USER2 are created with a DP callback. */ + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + NULL, + TEST_USER_PREFIX); + assert_non_null(req); + + tevent_req_set_callback(req, cache_req_user_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, 2); + + user_names = talloc_zero_array(test_ctx, const char *, num_users); + assert_non_null(user_names); + user_names[0] = sss_create_internal_fqname(user_names, users[0].short_name, + test_ctx->result->domain->name); + assert_non_null(user_names[0]); + user_names[1] = sss_create_internal_fqname(user_names, users[1].short_name, + test_ctx->result->domain->name); + assert_non_null(user_names[1]); + + for (int i = 0; i < num_users; ++i) { + ldb_results[i] = ldb_msg_find_attr_as_string(test_ctx->result->msgs[i], + SYSDB_NAME, NULL); + assert_non_null(ldb_results[i]); + } + + assert_string_not_equal(ldb_results[0], ldb_results[1]); + + assert_true(are_values_in_array(user_names, num_users, + ldb_results, num_users)); + + talloc_free(req_mem_ctx); + talloc_free(user_names); +} + +void test_users_by_filter_filter_old(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->create_user1 = true; + test_ctx->create_user2 = false; + + /* This user was updated in distant past, so it won't be reported by + * the filter search */ + prepare_user(test_ctx->tctx->dom, &users[1], 1000, 1); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_USER_PREFIX, NULL, ERR_OK); + + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + NULL, + TEST_USER_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_user_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, 1); + + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[0], + users[0].short_name); +} + +/* This test uses a "files" provider */ +void test_users_by_filter_filter_files(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->create_user1 = false; + test_ctx->create_user2 = false; + + /* This user was updated in distant past but will still be reported */ + prepare_user(test_ctx->tctx->dom, &users[0], 1000, 1); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters only go to DP when /etc/passwd and /etc/group were modified */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_USER_PREFIX, NULL, ERR_OK); + + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + NULL, + TEST_USER_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_user_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, 1); + + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[0], + users[0].short_name); +} +void test_users_by_filter_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_NO_USER_PREFIX, NULL, ERR_OK); + + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + NULL, + TEST_NO_USER_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_user_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_true(check_leaks_pop(req_mem_ctx)); +} + +/* + * Given two users are present + * When the users are searched by filtering domains + * Then the two users are returned correctly. + */ +static void test_users_by_filter_multiple_domains_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + size_t num_users = 2; + const char **input_dns = NULL; + const char **user_names = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->create_user1 = true; + test_ctx->create_user2 = true; + + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + /* Generate DN for user1 */ + input_dns = talloc_zero_array(test_ctx, const char *, num_users); + assert_non_null(input_dns); + input_dns[0] = talloc_asprintf(input_dns, "cn=%s,dc=test", + users[0].short_name); + assert_non_null(input_dns[0]); + + /* Generate internal FQDN for user1 */ + user_names = talloc_zero_array(test_ctx, const char *, num_users); + assert_non_null(user_names); + user_names[0] = sss_create_internal_fqname(user_names, users[0].short_name, + domain->name); + assert_non_null(user_names[0]); + + ret = sysdb_store_user(domain, user_names[0], "pwd", 1000, 1000, + NULL, NULL, NULL, input_dns[0], NULL, + NULL, 1000, time(NULL)); + assert_int_equal(ret, EOK); + + /* Generate DN for user2 */ + input_dns[1] = talloc_asprintf(input_dns, "cn=%s,dc=test", + users[1].short_name); + assert_non_null(input_dns[1]); + + /* Generate internal FQDN for user2 */ + user_names[1] = sss_create_internal_fqname(user_names, users[1].short_name, + domain->name); + assert_non_null(user_names[1]); + + ret = sysdb_store_user(domain, user_names[1], "pwd", 1001, 1001, + NULL, NULL, NULL, input_dns[1], NULL, + NULL, 1000, time(NULL)); + assert_int_equal(ret, EOK); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_USER_PREFIX, NULL, ERR_OK); + + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + NULL, + TEST_USER_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_user_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, num_users); + + for (int i = 0; i < num_users; ++i) { + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[i], + users[i].short_name); + } + + talloc_free(user_names); + talloc_free(input_dns); +} + +void test_users_by_filter_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + mock_parse_inp(TEST_NO_USER_PREFIX, NULL, ERR_OK); + + req = cache_req_user_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + domain->name, + NULL, + TEST_NO_USER_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_user_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_true(check_leaks_pop(req_mem_ctx)); +} + +static void cache_req_group_by_filter_test_done(struct tevent_req *req) +{ + struct cache_req_test_ctx *ctx = NULL; + + ctx = tevent_req_callback_data(req, struct cache_req_test_ctx); + + ctx->tctx->error = cache_req_group_by_filter_recv(ctx, req, &ctx->result); + talloc_zfree(req); + ctx->tctx->done = true; +} + +void test_group_by_recent_filter_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->create_group1 = true; + test_ctx->create_group2 = false; + + prepare_group(test_ctx->tctx->dom, &groups[1], 1001, time(NULL) - 1); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_USER_PREFIX, NULL, ERR_OK); + + /* Group TEST_GROUP is created with a DP callback. */ + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_USER_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_group_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, 1); + + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[0], + groups[0].short_name); +} + +void test_groups_by_recent_filter_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + TALLOC_CTX *tmp_ctx = NULL; + struct tevent_req *req = NULL; + const char **group_names = NULL; + const char **ldb_results = NULL; + const char *ldbname = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->create_group1 = true; + test_ctx->create_group2 = true; + + prepare_group(test_ctx->tctx->dom, &groups[1], 1001, time(NULL) - 1); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + mock_parse_inp(TEST_USER_PREFIX, NULL, ERR_OK); + + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Group TEST_GROUP1 and TEST_GROUP2 are created with a DP callback. */ + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_USER_PREFIX); + assert_non_null(req); + + tevent_req_set_callback(req, cache_req_group_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, 2); + + tmp_ctx = talloc_new(req_mem_ctx); + + group_names = talloc_array(tmp_ctx, const char *, 2); + assert_non_null(group_names); + group_names[0] = sss_create_internal_fqname(group_names, groups[0].short_name, + test_ctx->result->domain->name); + assert_non_null(group_names[0]); + group_names[1] = sss_create_internal_fqname(group_names, groups[1].short_name, + test_ctx->result->domain->name); + assert_non_null(group_names[1]); + + ldb_results = talloc_array(tmp_ctx, const char *, 2); + assert_non_null(ldb_results); + for (int i = 0; i < 2; ++i) { + ldbname = ldb_msg_find_attr_as_string(test_ctx->result->msgs[i], + SYSDB_NAME, NULL); + assert_non_null(ldbname); + ldb_results[i] = ldbname; + } + + assert_string_not_equal(ldb_results[0], ldb_results[1]); + + assert_true(tc_are_values_in_array(group_names, ldb_results)); + + talloc_zfree(tmp_ctx); +} + +void test_groups_by_filter_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_NO_GROUP_PREFIX, NULL, ERR_OK); + + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_NO_GROUP_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_group_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_true(check_leaks_pop(req_mem_ctx)); +} + +void test_groups_by_filter_files(void **state) +{ + struct cache_req_test_ctx *test_ctx; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->create_group1 = true; + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_GROUP_PREFIX, NULL, ERR_OK); + + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_GROUP_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_group_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, 1); + + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[0], + groups[0].short_name); +} + +/* + * Given two groups are present + * When the groups are searched by filtering domains + * Then the two groups are returned correctly. + */ +void test_groups_by_filter_multiple_domains_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + size_t num_groups = 2; + const char **group_names = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + test_ctx->create_group1 = true; + test_ctx->create_group2 = true; + + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + /* Generate internal FQDN for group1 */ + group_names = talloc_zero_array(test_ctx, const char *, num_groups); + assert_non_null(group_names); + group_names[0] = sss_create_internal_fqname(group_names, + groups[0].short_name, + domain->name); + assert_non_null(group_names[0]); + + ret = sysdb_store_group(domain, group_names[0], + 1000, NULL, 1000, time(NULL)); + assert_int_equal(ret, EOK); + + /* Generate internal FQDN for group2 */ + group_names[1] = sss_create_internal_fqname(group_names, + groups[1].short_name, + domain->name); + assert_non_null(group_names[1]); + + ret = sysdb_store_group(domain, group_names[1], + 1001, NULL, 1001, time(NULL)); + assert_int_equal(ret, EOK); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + mock_parse_inp(TEST_USER_PREFIX, NULL, ERR_OK); + + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + test_ctx->tctx->dom->name, + TEST_USER_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_group_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ERR_OK); + assert_true(check_leaks_pop(req_mem_ctx)); + + assert_non_null(test_ctx->result); + assert_int_equal(test_ctx->result->count, num_groups); + + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[0], + groups[1].short_name); + + assert_msg_has_shortname(test_ctx, + test_ctx->result->msgs[1], + groups[0].short_name); + + talloc_free(group_names); +} + +void test_groups_by_filter_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + TALLOC_CTX *req_mem_ctx = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + req_mem_ctx = talloc_new(global_talloc_context); + check_leaks_push(req_mem_ctx); + + /* Filters always go to DP */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + mock_parse_inp(TEST_NO_GROUP_PREFIX, NULL, ERR_OK); + + req = cache_req_group_by_filter_send(req_mem_ctx, test_ctx->tctx->ev, + test_ctx->rctx, + CACHE_REQ_POSIX_DOM, + domain->name, + TEST_NO_GROUP_PREFIX); + assert_non_null(req); + tevent_req_set_callback(req, cache_req_group_by_filter_test_done, test_ctx); + + ret = test_ev_loop(test_ctx->tctx); + assert_int_equal(ret, ENOENT); + assert_true(check_leaks_pop(req_mem_ctx)); +} + +void test_object_by_sid_user_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 1000, time(NULL)); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + users[0].sid, attrs, 0, ERR_OK); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_object_by_sid_user_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], -1000, time(NULL)); + + /* Mock values. */ + /* DP should be contacted */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + users[0].sid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_object_by_sid_user_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 50, time(NULL) - 26); + + /* Mock values. */ + /* DP should be contacted without callback */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + users[0].sid, attrs, 50, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_object_by_sid_user_ncache(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + ret = sss_ncache_set_sid(test_ctx->ncache, false, test_ctx->tctx->dom, users[0].sid); + assert_int_equal(ret, EOK); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + users[0].sid, attrs, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_object_by_sid_user_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + test_ctx->create_user1 = true; + test_ctx->create_user2 = false; + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + users[0].sid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_object_by_sid_user_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + users[0].sid, attrs, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_object_by_sid_user_multiple_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_user(domain, &users[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_sid(test_ctx, NULL, users[0].sid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); +} + +void test_object_by_sid_user_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_sid(test_ctx, NULL, users[0].sid, attrs, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_object_by_sid_group_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_group(test_ctx->tctx->dom, &groups[0], 1000, time(NULL)); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + groups[0].sid, attrs, 0, ERR_OK); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_object_by_sid_group_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_group(test_ctx->tctx->dom, &groups[0], -1000, time(NULL)); + + /* Mock values. */ + /* DP should be contacted */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + groups[0].sid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_object_by_sid_group_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_group(test_ctx->tctx->dom, &groups[0], 50, time(NULL) - 26); + + /* Mock values. */ + /* DP should be contacted without callback */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + groups[0].sid, attrs, 50, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_object_by_sid_group_ncache(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + ret = sss_ncache_set_sid(test_ctx->ncache, false, test_ctx->tctx->dom, groups[0].sid); + assert_int_equal(ret, EOK); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + groups[0].sid, attrs, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_object_by_sid_group_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + test_ctx->create_group1 = true; + test_ctx->create_group2 = false; + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + groups[0].sid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_object_by_sid_group_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_object_by_sid(test_ctx, test_ctx->tctx->dom, + groups[0].sid, attrs, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_object_by_sid_group_multiple_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_group(domain, &groups[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_sid(test_ctx, NULL, groups[0].sid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); +} + +void test_object_by_sid_group_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_sid(test_ctx, NULL, groups[0].sid, attrs, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_object_by_id_user_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 1000, time(NULL)); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_object_by_id_user_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], -1000, time(NULL)); + + /* Mock values. */ + /* DP should be contacted */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_object_by_id_user_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_user(test_ctx->tctx->dom, &users[0], 50, time(NULL) - 26); + + /* Mock values. */ + /* DP should be contacted without callback */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 50, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_object_by_id_user_ncache(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. We explicitly add the UID into BOTH UID and GID + * namespaces, because otherwise the cache_req plugin would + * search the Data Provider anyway, because it can't be sure + * the object can be of the other type or not + */ + ret = sss_ncache_set_uid(test_ctx->ncache, + false, + test_ctx->tctx->dom, + users[0].uid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_set_gid(test_ctx->ncache, + false, + test_ctx->tctx->dom, + users[0].uid); + assert_int_equal(ret, EOK); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_object_by_id_user_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + test_ctx->create_user1 = true; + test_ctx->create_user2 = false; + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], test_ctx->tctx->dom); +} + +void test_object_by_id_user_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_object_by_id_user_multiple_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_user(domain, &users[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); +} + +void test_object_by_id_user_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_object_by_id_group_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_group(test_ctx->tctx->dom, &groups[0], 1000, time(NULL)); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_object_by_id_group_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_group(test_ctx->tctx->dom, &groups[0], -1000, time(NULL)); + + /* Mock values. */ + /* DP should be contacted */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_object_by_id_group_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + prepare_group(test_ctx->tctx->dom, &groups[0], 50, time(NULL) - 26); + + /* Mock values. */ + /* DP should be contacted without callback */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 50, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_object_by_id_group_ncache(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + errno_t ret; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup group. We explicitly add the UID into BOTH UID and GID + * namespaces, because otherwise the cache_req plugin would + * search the Data Provider anyway, because it can't be sure + * the object can be of the other type or not + */ + ret = sss_ncache_set_uid(test_ctx->ncache, + false, + test_ctx->tctx->dom, + groups[0].gid); + assert_int_equal(ret, EOK); + + ret = sss_ncache_set_gid(test_ctx->ncache, + false, + test_ctx->tctx->dom, + groups[0].gid); + assert_int_equal(ret, EOK); + + assert_int_equal(ret, EOK); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_object_by_id_group_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + test_ctx->create_group1 = true; + test_ctx->create_group2 = false; + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], test_ctx->tctx->dom); +} + +void test_object_by_id_group_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + mock_account_recv_simple(); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_object_by_id_group_multiple_domains_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + + prepare_group(domain, &groups[0], 1000, time(NULL)); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); +} + +void test_object_by_id_group_multiple_domains_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_GRSRC_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + /* Mock values. */ + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ENOENT); + assert_true(test_ctx->dp_called); +} + +void test_object_by_id_user_multiple_domains_locator_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d"); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_user(domain, &users[0], 1000, time(NULL)); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, EOK); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK); + /* Even though the locator tells us to skip all domains except d, the domains + * are standalone and the result of the locator request is only valid within + * the subdomains + */ + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_user_multiple_domains_locator_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d"); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_user(domain, &users[0], -1000, time(NULL)); + + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, EOK); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_user_sub_domains_locator_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_user(domain, &users[0], 1000, time(NULL)); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK); + + /* Even though the ID is present in the last domain, + * we're not calling sss_dp_get_account_send, + * because the locator will cause cache_req to skip + * all domains except _d + */ + assert_false(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_user_sub_domains_locator_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_user(domain, &users[0], -1000, time(NULL)); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_user_sub_domains_locator_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_user(domain, &users[0], 50, time(NULL) - 26); + + /* Note - DP will only be called once and we're not waiting + * for the results (so, we're not mocking _recv) + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 50, ERR_OK); + + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_user_sub_domains_locator_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + test_ctx->create_subuser1 = true; + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_user_sub_domains_locator_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + will_return(sss_dp_get_account_domain_recv, ERR_NOT_FOUND); + + /* The test won't even ask the DP for the object, just iterate + * over the domains using the negative cache and quit + */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_object_by_id_user_sub_domains_locator_cache_expired_two_calls(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup user. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + test_ctx->create_subuser1 = true; + prepare_user(domain, &users[0], -1000, time(NULL)); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, EOK); + assert_true(test_ctx->dp_called); + check_user(test_ctx, &users[0], domain); + + /* Request the same user again */ + test_ctx->tctx->done = false; + talloc_zfree(test_ctx->result); + + run_object_by_id(test_ctx, NULL, users[0].uid, attrs, 0, EOK); + check_user(test_ctx, &users[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_group_multiple_domains_locator_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d"); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 1000, time(NULL)); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, EOK); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK); + /* Even though the locator tells us to skip all domains except d, the domains + * are standalone and the result of the locator request is only valid within + * the subdomains + */ + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_group_multiple_domains_locator_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, "responder_cache_req_test_d"); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + "responder_cache_req_test_d", true); + assert_non_null(domain); + prepare_group(domain, &groups[0], -1000, time(NULL)); + + will_return_always(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, EOK); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + will_return_always(sss_dp_get_account_domain_recv, ERR_GET_ACCT_DOM_NOT_SUPPORTED); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_group_sub_domains_locator_cache_valid(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 1000, time(NULL)); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK); + + /* Even though the ID is present in the last domain, + * we're not calling sss_dp_get_account_send, + * because the locator will cause cache_req to skip + * all domains except _d + */ + assert_false(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_group_sub_domains_locator_cache_expired(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_group(domain, &groups[0], -1000, time(NULL)); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_group_sub_domains_locator_cache_midpoint(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + prepare_group(domain, &groups[0], 50, time(NULL) - 26); + + /* Note - DP will only be called once and we're not waiting + * for the results (so, we're not mocking _recv) + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 50, ERR_OK); + + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_group_sub_domains_locator_missing_found(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + test_ctx->create_subgroup1 = true; + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ERR_OK); + + assert_true(test_ctx->dp_called); + + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +void test_object_by_id_group_sub_domains_locator_missing_notfound(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + will_return(sss_dp_get_account_domain_recv, ERR_NOT_FOUND); + + /* The test won't even ask the DP for the object, just iterate + * over the domains using the negative cache and quit + */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, ENOENT); + assert_false(test_ctx->dp_called); +} + +void test_object_by_id_group_sub_domains_locator_cache_expired_two_calls(void **state) +{ + struct cache_req_test_ctx *test_ctx = NULL; + struct sss_domain_info *domain = NULL; + const char *locator_domain; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = SYSDB_PW_ATTRS; + + test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx); + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + /* Has to be a talloc ptr, not just const, so it's stealable inside cache_req */ + locator_domain = talloc_strdup(tmp_ctx, subdomain_name); + assert_non_null(locator_domain); + + /* Setup group. */ + domain = find_domain_by_name(test_ctx->tctx->dom, + subdomain_name, + true); + assert_non_null(domain); + test_ctx->create_subgroup1 = true; + prepare_group(domain, &groups[0], -1000, time(NULL)); + + /* Note - DP will only be called once (so, we're not using will_return_always) + * because the locator will tell us which domain to look into. For the recv + * function, we use always b/c internally it mocks several values. + */ + will_return(__wrap_sss_dp_get_account_send, test_ctx); + will_return_always(sss_dp_get_account_recv, 0); + + will_return(sss_dp_get_account_domain_recv, EOK); + will_return(sss_dp_get_account_domain_recv, locator_domain); + + /* Test. */ + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, EOK); + assert_true(test_ctx->dp_called); + check_group(test_ctx, &groups[0], domain); + + /* Request the same group again */ + test_ctx->tctx->done = false; + talloc_zfree(test_ctx->result); + + run_object_by_id(test_ctx, NULL, groups[0].gid, attrs, 0, EOK); + check_group(test_ctx, &groups[0], domain); + + talloc_free(tmp_ctx); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + new_single_domain_test(user_by_name_cache_valid), + new_single_domain_test(user_by_name_cache_expired), + new_single_domain_test(user_by_name_cache_midpoint), + new_single_domain_test(user_by_name_ncache), + new_single_domain_test(user_by_name_missing_found), + new_single_domain_test(user_by_name_missing_notfound), + new_single_domain_test(user_by_name_missing_notfound_cache_first), + new_single_domain_test(user_by_name_missing_notfound_full_name), + new_single_domain_test(user_by_name_missing_notfound_cache_first_full_name), + new_multi_domain_test(user_by_name_multiple_domains_found), + new_multi_domain_test(user_by_name_multiple_domains_notfound), + new_multi_domain_test(user_by_name_multiple_domains_parse), + new_multi_domain_test(user_by_name_multiple_domains_requested_domains_found), + new_multi_domain_test(user_by_name_multiple_domains_requested_domains_notfound), + + new_single_domain_test(user_by_upn_cache_valid), + new_single_domain_test(user_by_upn_cache_expired), + new_single_domain_test(user_by_upn_cache_midpoint), + new_single_domain_test(user_by_upn_ncache), + new_single_domain_test(user_by_upn_missing_found), + new_single_domain_test(user_by_upn_missing_notfound), + new_multi_domain_test(user_by_upn_multiple_domains_found), + new_multi_domain_test(user_by_upn_multiple_domains_notfound), + + new_single_domain_test(user_by_id_cache_valid), + new_single_domain_test(user_by_id_cache_expired), + new_single_domain_test(user_by_id_cache_midpoint), + new_single_domain_test(user_by_id_ncache), + new_single_domain_test(user_by_id_missing_found), + new_single_domain_test(user_by_id_missing_notfound), + new_multi_domain_test(user_by_id_multiple_domains_found), + new_multi_domain_test(user_by_id_multiple_domains_notfound), + new_single_domain_id_limit_test(user_by_id_below_id_range), + new_single_domain_id_limit_test(user_by_id_above_id_range), + + new_single_domain_test(group_by_name_cache_valid), + new_single_domain_test(group_by_name_cache_expired), + new_single_domain_test(group_by_name_cache_midpoint), + new_single_domain_test(group_by_name_ncache), + new_single_domain_test(group_by_name_missing_found), + new_single_domain_test(group_by_name_missing_notfound), + new_multi_domain_test(group_by_name_multiple_domains_found), + new_multi_domain_test(group_by_name_multiple_domains_notfound), + new_multi_domain_test(group_by_name_multiple_domains_parse), + new_single_domain_id_limit_test(group_by_id_below_id_range), + new_single_domain_id_limit_test(group_by_id_above_id_range), + + new_single_domain_test(group_by_id_cache_valid), + new_single_domain_test(group_by_id_cache_expired), + new_single_domain_test(group_by_id_cache_midpoint), + new_single_domain_test(group_by_id_ncache), + new_single_domain_test(group_by_id_missing_found), + new_single_domain_test(group_by_id_missing_notfound), + new_multi_domain_test(group_by_id_multiple_domains_found), + new_multi_domain_test(group_by_id_multiple_domains_notfound), + new_multi_domain_test(group_by_id_multiple_domains_outside_id_range), + + new_multi_domain_test(group_by_id_multiple_domains_locator_cache_valid), + new_multi_domain_test(group_by_id_multiple_domains_locator_cache_expired), + new_subdomain_test(group_by_id_sub_domains_locator_cache_valid), + new_subdomain_test(group_by_id_sub_domains_locator_cache_expired), + new_subdomain_test(group_by_id_sub_domains_locator_cache_midpoint), + new_subdomain_test(group_by_id_sub_domains_locator_missing_found), + new_subdomain_test(group_by_id_sub_domains_locator_missing_notfound), + new_subdomain_test(group_by_id_sub_domains_locator_cache_expired_two_calls), + + new_multi_domain_test(user_by_id_multiple_domains_locator_cache_valid), + new_multi_domain_test(user_by_id_multiple_domains_locator_cache_expired), + new_subdomain_test(user_by_id_sub_domains_locator_cache_valid), + new_subdomain_test(user_by_id_sub_domains_locator_cache_expired), + new_subdomain_test(user_by_id_sub_domains_locator_cache_midpoint), + new_subdomain_test(user_by_id_sub_domains_locator_missing_found), + new_subdomain_test(user_by_id_sub_domains_locator_missing_notfound), + new_subdomain_test(user_by_id_sub_domains_locator_cache_expired_two_calls), + + new_single_domain_test(user_by_recent_filter_valid), + new_single_domain_test(users_by_recent_filter_valid), + new_single_domain_test(group_by_recent_filter_valid), + new_single_domain_test(groups_by_recent_filter_valid), + + new_single_domain_test(users_by_filter_filter_old), +#ifdef BUILD_FILES_PROVIDER + new_files_domain_test(users_by_filter_filter_files), +#endif + new_single_domain_test(users_by_filter_notfound), + new_multi_domain_test(users_by_filter_multiple_domains_valid), + new_multi_domain_test(users_by_filter_multiple_domains_notfound), +#ifdef BUILD_FILES_PROVIDER + new_files_domain_test(groups_by_filter_files), +#endif + new_single_domain_test(groups_by_filter_notfound), + new_multi_domain_test(groups_by_filter_multiple_domains_valid), + new_multi_domain_test(groups_by_filter_multiple_domains_notfound), + + new_single_domain_test(object_by_sid_user_cache_valid), + new_single_domain_test(object_by_sid_user_cache_expired), + new_single_domain_test(object_by_sid_user_cache_midpoint), + new_single_domain_test(object_by_sid_user_ncache), + new_single_domain_test(object_by_sid_user_missing_found), + new_single_domain_test(object_by_sid_user_missing_notfound), + new_multi_domain_test(object_by_sid_user_multiple_domains_found), + new_multi_domain_test(object_by_sid_user_multiple_domains_notfound), + + new_single_domain_test(object_by_sid_group_cache_valid), + new_single_domain_test(object_by_sid_group_cache_expired), + new_single_domain_test(object_by_sid_group_cache_midpoint), + new_single_domain_test(object_by_sid_group_ncache), + new_single_domain_test(object_by_sid_group_missing_found), + new_single_domain_test(object_by_sid_group_missing_notfound), + new_multi_domain_test(object_by_sid_group_multiple_domains_found), + new_multi_domain_test(object_by_sid_group_multiple_domains_notfound), + + new_single_domain_test(object_by_id_user_cache_valid), + new_single_domain_test(object_by_id_user_cache_expired), + new_single_domain_test(object_by_id_user_cache_midpoint), + new_single_domain_test(object_by_id_user_ncache), + new_single_domain_test(object_by_id_user_missing_found), + new_single_domain_test(object_by_id_user_missing_notfound), + new_multi_domain_test(object_by_id_user_multiple_domains_found), + new_multi_domain_test(object_by_id_user_multiple_domains_notfound), + + new_single_domain_test(object_by_id_group_cache_valid), + new_single_domain_test(object_by_id_group_cache_expired), + new_single_domain_test(object_by_id_group_cache_midpoint), + new_single_domain_test(object_by_id_group_ncache), + new_single_domain_test(object_by_id_group_missing_found), + new_single_domain_test(object_by_id_group_missing_notfound), + new_multi_domain_test(object_by_id_group_multiple_domains_found), + new_multi_domain_test(object_by_id_group_multiple_domains_notfound), + + new_multi_domain_test(object_by_id_user_multiple_domains_locator_cache_valid), + new_multi_domain_test(object_by_id_user_multiple_domains_locator_cache_expired), + new_subdomain_test(object_by_id_user_sub_domains_locator_cache_valid), + new_subdomain_test(object_by_id_user_sub_domains_locator_cache_expired), + new_subdomain_test(object_by_id_user_sub_domains_locator_cache_midpoint), + new_subdomain_test(object_by_id_user_sub_domains_locator_missing_found), + new_subdomain_test(object_by_id_user_sub_domains_locator_missing_notfound), + new_subdomain_test(object_by_id_user_sub_domains_locator_cache_expired_two_calls), + + new_multi_domain_test(object_by_id_group_multiple_domains_locator_cache_valid), + new_multi_domain_test(object_by_id_group_multiple_domains_locator_cache_expired), + new_subdomain_test(object_by_id_group_sub_domains_locator_cache_valid), + new_subdomain_test(object_by_id_group_sub_domains_locator_cache_expired), + new_subdomain_test(object_by_id_group_sub_domains_locator_cache_midpoint), + new_subdomain_test(object_by_id_group_sub_domains_locator_missing_found), + new_subdomain_test(object_by_id_group_sub_domains_locator_missing_notfound), + new_subdomain_test(object_by_id_group_sub_domains_locator_cache_expired_two_calls), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_responder_common.c b/src/tests/cmocka/test_responder_common.c new file mode 100644 index 0000000..2935625 --- /dev/null +++ b/src/tests/cmocka/test_responder_common.c @@ -0,0 +1,358 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: Common responder code tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_responder_conf.ldb" +#define TEST_DOM_NAME "responder_test" +#define TEST_ID_PROVIDER "ldap" + +#define NAME "username" + +/* register_cli_protocol_version is required in test since it links with + * responder_common.c module + */ +struct cli_protocol_version *register_cli_protocol_version(void) +{ + static struct cli_protocol_version responder_test_cli_protocol_version[] = { + { 0, NULL, NULL } + }; + + return responder_test_cli_protocol_version; +} + +struct parse_inp_test_ctx { + struct sss_test_ctx *tctx; + struct resp_ctx *rctx; +}; + +static int parse_inp_test_setup(void **state) +{ + struct parse_inp_test_ctx *parse_inp_ctx; + int ret; + + assert_true(leak_check_setup()); + parse_inp_ctx = talloc_zero(global_talloc_context, struct parse_inp_test_ctx); + assert_non_null(parse_inp_ctx); + + parse_inp_ctx->tctx = create_dom_test_ctx(parse_inp_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, NULL); + assert_non_null(parse_inp_ctx->tctx); + + parse_inp_ctx->rctx = mock_rctx(parse_inp_ctx, + parse_inp_ctx->tctx->ev, + parse_inp_ctx->tctx->dom, + parse_inp_ctx); + assert_non_null(parse_inp_ctx->rctx); + + /* Testing the request race condition should be a special case */ + gettimeofday(&parse_inp_ctx->rctx->get_domains_last_call, NULL); + + /* sysdb_master_domain_update sets the view name, if we do not call it + * here we get a leak check warning when sysdb_master_domain_update is + * called later while processing the tests. */ + ret = sysdb_master_domain_update(parse_inp_ctx->tctx->dom); + assert_int_equal(ret, EOK); + + check_leaks_push(parse_inp_ctx); + *state = parse_inp_ctx; + return 0; +} + +static int parse_inp_test_teardown(void **state) +{ + struct parse_inp_test_ctx *parse_inp_ctx = talloc_get_type(*state, + struct parse_inp_test_ctx); + + assert_true(check_leaks_pop(parse_inp_ctx) == true); + + talloc_free(parse_inp_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +int __real_sss_parse_name_for_domains(TALLOC_CTX *memctx, + struct sss_domain_info *domains, + const char *default_domain, + const char *orig, char **domain, char **name); + +int __wrap_sss_parse_name_for_domains(TALLOC_CTX *memctx, + struct sss_domain_info *domains, + const char *default_domain, + const char *orig, char **domain, char **name) +{ + enum sss_test_wrapper_call wtype = sss_mock_type(enum sss_test_wrapper_call); + errno_t ret; + + if (wtype == WRAP_CALL_REAL) { + return __real_sss_parse_name_for_domains(memctx, domains, + default_domain, orig, + domain, name); + } + + ret = sss_mock_type(errno_t); + return ret; +} + +void parse_inp_simple_done(struct tevent_req *req) +{ + errno_t ret; + struct parse_inp_test_ctx *parse_inp_ctx = + tevent_req_callback_data(req, struct parse_inp_test_ctx); + char *name = NULL; + char *domname = NULL; + + ret = sss_parse_inp_recv(req, parse_inp_ctx, &name, &domname); + assert_int_equal(ret, EOK); + + test_ev_done(parse_inp_ctx->tctx, EOK); + talloc_free(req); + + assert_string_equal(name, NAME); + assert_null(domname); + talloc_free(name); +} + +void parse_inp_simple(void **state) +{ + struct parse_inp_test_ctx *parse_inp_ctx = talloc_get_type(*state, + struct parse_inp_test_ctx); + struct tevent_req *req; + errno_t ret; + + will_return(__wrap_sss_parse_name_for_domains, WRAP_CALL_REAL); + + req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, + parse_inp_ctx->rctx->default_domain, NAME); + assert_non_null(req); + tevent_req_set_callback(req, parse_inp_simple_done, parse_inp_ctx); + + ret = test_ev_loop(parse_inp_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void parse_inp_call_dp(void **state) +{ + struct parse_inp_test_ctx *parse_inp_ctx = talloc_get_type(*state, + struct parse_inp_test_ctx); + struct tevent_req *req; + errno_t ret; + + /* First call will indicate we need to go to DP */ + will_return(__wrap_sss_parse_name_for_domains, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_parse_name_for_domains, EAGAIN); + /* The second one will succeed as the domains are up-to-date */ + will_return(__wrap_sss_parse_name_for_domains, WRAP_CALL_REAL); + + req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, + parse_inp_ctx->rctx->default_domain, NAME); + assert_non_null(req); + tevent_req_set_callback(req, parse_inp_simple_done, parse_inp_ctx); + + ret = test_ev_loop(parse_inp_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void parse_inp_call_attach(void **state) +{ + struct parse_inp_test_ctx *parse_inp_ctx = talloc_get_type(*state, + struct parse_inp_test_ctx); + struct tevent_req *req; + errno_t ret; + + /* simulate responder startup */ + parse_inp_ctx->rctx->get_domains_last_call.tv_sec = 0; + + /* The first parse wouldn't be called, the second one will succeed + * as the domains are up-to-date */ + will_return(__wrap_sss_parse_name_for_domains, WRAP_CALL_REAL); + + req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, + parse_inp_ctx->rctx->default_domain, NAME); + assert_non_null(req); + tevent_req_set_callback(req, parse_inp_simple_done, parse_inp_ctx); + + ret = test_ev_loop(parse_inp_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void parse_inp_neg_done(struct tevent_req *req) +{ + errno_t ret; + struct parse_inp_test_ctx *parse_inp_ctx = + tevent_req_callback_data(req, struct parse_inp_test_ctx); + char *name = NULL; + char *domname = NULL; + + ret = sss_parse_inp_recv(req, parse_inp_ctx, &name, &domname); + assert_int_equal(ret, ERR_INPUT_PARSE); + test_ev_done(parse_inp_ctx->tctx, EOK); + talloc_free(req); + + assert_null(name); + assert_null(domname); +} + +void parse_inp_call_neg(void **state) +{ + struct parse_inp_test_ctx *parse_inp_ctx = talloc_get_type(*state, + struct parse_inp_test_ctx); + struct tevent_req *req; + errno_t ret; + + /* Simulate an error */ + will_return(__wrap_sss_parse_name_for_domains, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_parse_name_for_domains, EINVAL); + + req = sss_parse_inp_send(parse_inp_ctx, parse_inp_ctx->rctx, + parse_inp_ctx->rctx->default_domain, NAME); + assert_non_null(req); + tevent_req_set_callback(req, parse_inp_neg_done, parse_inp_ctx); + + ret = test_ev_loop(parse_inp_ctx->tctx); + assert_int_equal(ret, EOK); +} + +struct sss_nc_ctx { + struct parse_inp_test_ctx *pctx; +}; + +errno_t __wrap_sss_ncache_reset_repopulate_permanent(struct resp_ctx *rctx, + struct sss_nc_ctx *dummy_ncache_ptr) +{ + test_ev_done(dummy_ncache_ptr->pctx->tctx, EOK); + return EOK; +} + +void test_schedule_get_domains_task(void **state) +{ + struct parse_inp_test_ctx *parse_inp_ctx = talloc_get_type(*state, + struct parse_inp_test_ctx); + errno_t ret; + struct sss_nc_ctx *dummy_ncache_ptr; + + dummy_ncache_ptr = talloc(parse_inp_ctx, struct sss_nc_ctx); + assert_non_null(dummy_ncache_ptr); + dummy_ncache_ptr->pctx = parse_inp_ctx; + + ret = schedule_get_domains_task(dummy_ncache_ptr, + parse_inp_ctx->rctx->ev, + parse_inp_ctx->rctx, + dummy_ncache_ptr, NULL, NULL); + assert_int_equal(ret, EOK); + + ret = test_ev_loop(parse_inp_ctx->tctx); + assert_int_equal(ret, EOK); + talloc_free(dummy_ncache_ptr); +} + +void test_sss_output_fqname(void **state) +{ + struct parse_inp_test_ctx *parse_inp_ctx = talloc_get_type(*state, + struct parse_inp_test_ctx); + errno_t ret; + struct sized_string *res = NULL; + + ret = sized_output_name(parse_inp_ctx, parse_inp_ctx->rctx, "dummy", + parse_inp_ctx->tctx->dom, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + assert_string_equal("dummy", res->str); + assert_int_equal(6, res->len); + + talloc_zfree(res); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(parse_inp_simple, + parse_inp_test_setup, + parse_inp_test_teardown), +// TODO fix these tests, see: +// https://github.com/SSSD/sssd/issues/4801 +// cmocka_unit_test_setup_teardown(parse_inp_call_dp, +// parse_inp_test_setup, +// parse_inp_test_teardown), +// cmocka_unit_test_setup_teardown(parse_inp_call_attach, +// parse_inp_test_setup, +// parse_inp_test_teardown), + cmocka_unit_test_setup_teardown(parse_inp_call_neg, + parse_inp_test_setup, + parse_inp_test_teardown), + cmocka_unit_test_setup_teardown(test_schedule_get_domains_task, + parse_inp_test_setup, + parse_inp_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_output_fqname, + parse_inp_test_setup, + parse_inp_test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + return rv; +} diff --git a/src/tests/cmocka/test_sdap.c b/src/tests/cmocka/test_sdap.c new file mode 100644 index 0000000..047591e --- /dev/null +++ b/src/tests/cmocka/test_sdap.c @@ -0,0 +1,1329 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "providers/ldap/ldap_opts.h" +#include "providers/ipa/ipa_opts.h" +#include "util/crypto/sss_crypto.h" +#include "db/sysdb_iphosts.h" + +/* mock an LDAP entry */ +struct mock_ldap_attr { + const char *name; + const char **values; +}; + +struct mock_ldap_entry { + const char *dn; + struct mock_ldap_attr *attrs; +}; + +struct mock_ldap_entry *global_ldap_entry; + +static int mock_ldap_entry_iter(void) +{ + return sss_mock_type(int); +} + +static struct mock_ldap_entry *mock_ldap_entry_get(void) +{ + return sss_mock_ptr_type(struct mock_ldap_entry *); +} + +void set_entry_parse(struct mock_ldap_entry *entry) +{ + will_return_always(mock_ldap_entry_get, entry); +} + +LDAPDerefRes *mock_deref_res(TALLOC_CTX *mem_ctx, + struct mock_ldap_entry *entry) +{ + LDAPDerefRes *dref; + LDAPDerefVal *dval, *dvaltail = NULL; + size_t nattr; + size_t nval; + + dref = talloc_zero(mem_ctx, LDAPDerefRes); + assert_non_null(dref); + + dref->derefVal.bv_val = talloc_strdup(dref, entry->dn); + assert_non_null(dref->derefVal.bv_val); + dref->derefVal.bv_len = strlen(entry->dn); + + if (entry->attrs == NULL) { + /* no attributes, done */ + return dref; + } + + for (nattr = 0; entry->attrs[nattr].name; nattr++) { + dval = talloc_zero(dref, LDAPDerefVal); + assert_non_null(dval); + + dval->type = talloc_strdup(dval, entry->attrs[nattr].name); + assert_non_null(dval->type); + + for (nval = 0; entry->attrs[nattr].values[nval]; nval++); + + dval->vals = talloc_zero_array(dval, struct berval, nval+1); + assert_non_null(dval->vals); + for (nval = 0; entry->attrs[nattr].values[nval]; nval++) { + dval->vals[nval].bv_val = talloc_strdup(dval->vals, + entry->attrs[nattr].values[nval]); + assert_non_null(dval->vals[nval].bv_val); + dval->vals[nval].bv_len = strlen(dval->vals[nval].bv_val); + } + + if (dvaltail != NULL) { + dvaltail->next = dval; + dvaltail = dvaltail->next; + } else { + dvaltail = dval; + dref->attrVals = dval; + } + } + + return dref; +} + +/* libldap wrappers */ +int __wrap_ldap_set_option(LDAP *ld, + int option, + void *invalue) +{ + return LDAP_OPT_SUCCESS; +} + +char *__wrap_ldap_get_dn(LDAP *ld, LDAPMessage *entry) +{ + struct mock_ldap_entry *ldap_entry = mock_ldap_entry_get(); + return discard_const(ldap_entry->dn); +} + +void __wrap_ldap_memfree(void *p) +{ + return; +} + +struct berval **__wrap_ldap_get_values_len(LDAP *ld, + LDAPMessage *entry, + LDAP_CONST char *target) +{ + size_t count, i; + struct berval **vals; + const char **attrvals; + struct mock_ldap_entry *ldap_entry = mock_ldap_entry_get(); + + if (target == NULL) return NULL; + if (ldap_entry == NULL) return NULL; + /* Should we return empty array here? */ + if (ldap_entry->attrs == NULL) return NULL; + + attrvals = NULL; + for (i = 0; ldap_entry->attrs[i].name != NULL; i++) { + if (strcmp(ldap_entry->attrs[i].name, target) == 0) { + attrvals = ldap_entry->attrs[i].values; + break; + } + } + + if (attrvals == NULL) { + return NULL; + } + + count = 0; + for (i = 0; attrvals[i]; i++) { + count++; + } + + vals = talloc_zero_array(global_talloc_context, + struct berval *, + count + 1); + assert_non_null(vals); + + for (i = 0; attrvals[i]; i++) { + vals[i] = talloc_zero(vals, struct berval); + assert_non_null(vals[i]); + + vals[i]->bv_val = talloc_strdup(vals[i], attrvals[i]); + if (vals[i]->bv_val == NULL) { + talloc_free(vals); + return NULL; + } + vals[i]->bv_len = strlen(attrvals[i]); + } + + return vals; +} + +void __wrap_ldap_value_free_len(struct berval **vals) +{ + talloc_free(vals); /* Allocated on global_talloc_context */ +} + +char *__wrap_ldap_first_attribute(LDAP *ld, + LDAPMessage *entry, + BerElement **berout) +{ + struct mock_ldap_entry *ldap_entry = mock_ldap_entry_get(); + + if (ldap_entry == NULL) return NULL; + if (ldap_entry->attrs == NULL) return NULL; + + will_return(mock_ldap_entry_iter, 1); + return discard_const(ldap_entry->attrs[0].name); +} + +char *__wrap_ldap_next_attribute(LDAP *ld, + LDAPMessage *entry, + BerElement *ber) +{ + struct mock_ldap_entry *ldap_entry = mock_ldap_entry_get(); + + int idx = mock_ldap_entry_iter(); + char *val; + + val = discard_const(ldap_entry->attrs[idx].name); + if (val != NULL) { + will_return(mock_ldap_entry_iter, idx + 1); + } + return val; +} + +/* Mock parsing search base without overlinking the test */ +errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, + struct dp_option *opts, int class, + struct sdap_search_base ***_search_bases) +{ + return EOK; +} + +/* Utility function */ +void assert_entry_has_attr(struct sysdb_attrs *attrs, + const char *attr, + const char *value) +{ + const char *v; + int ret; + + ret = sysdb_attrs_get_string(attrs, attr, &v); + assert_int_equal(ret, ERR_OK); + assert_non_null(v); + assert_string_equal(v, value); +} + +void assert_entry_has_no_attr(struct sysdb_attrs *attrs, + const char *attr) +{ + int ret; + const char *v; + ret = sysdb_attrs_get_string(attrs, attr, &v); + assert_int_equal(ret, ENOENT); +} + +struct parse_test_ctx { + struct sdap_handle sh; + struct sdap_msg sm; +}; + +static int parse_entry_test_setup(void **state) +{ + struct parse_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct parse_test_ctx); + assert_non_null(test_ctx); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int parse_entry_test_teardown(void **state) +{ + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void test_parse_with_map(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct mock_ldap_entry test_ipa_user; + struct sdap_attr_map *map; + struct ldb_message_element *el; + uint8_t *decoded_key; + size_t key_len; + + const char *oc_values[] = { "posixAccount", NULL }; + const char *uid_values[] = { "tuser1", NULL }; + const char *extra_values[] = { "extra", NULL }; + const char *multi_values[] = { "svc1", "svc2", NULL }; + const char *ssh_values[] = { "1234", NULL }; + struct mock_ldap_attr test_ipa_user_attrs[] = { + { .name = "objectClass", .values = oc_values }, + { .name = "uid", .values = uid_values }, + { .name = "extra", .values = extra_values }, + { .name = "authorizedService", .values = multi_values }, + { .name = "ipaSshPubKey", .values = ssh_values }, + { NULL, NULL } + }; + + test_ipa_user.dn = "cn=testuser,dc=example,dc=com"; + test_ipa_user.attrs = test_ipa_user_attrs; + set_entry_parse(&test_ipa_user); + + ret = sdap_copy_map(test_ctx, ipa_user_map, SDAP_OPTS_USER, &map); + assert_int_equal(ret, ERR_OK); + + ret = sdap_parse_entry(test_ctx, &test_ctx->sh, &test_ctx->sm, + map, SDAP_OPTS_USER, + &attrs, false); + assert_int_equal(ret, ERR_OK); + + assert_int_equal(attrs->num, 4); + + /* Every entry has a DN */ + assert_entry_has_attr(attrs, SYSDB_ORIG_DN, + "cn=testuser,dc=example,dc=com"); + /* Test the single-valued attribute */ + assert_entry_has_attr(attrs, SYSDB_NAME, "tuser1"); + + /* Multivalued attributes must return all values */ + ret = sysdb_attrs_get_el_ext(attrs, SYSDB_AUTHORIZED_SERVICE, false, &el); + assert_int_equal(ret, ERR_OK); + assert_int_equal(el->num_values, 2); + assert_true((strcmp((const char *) el->values[0].data, "svc1") == 0 && + strcmp((const char *) el->values[1].data, "svc2") == 0) || + (strcmp((const char *) el->values[1].data, "svc1") == 0 && + strcmp((const char *) el->values[0].data, "svc2") == 0)); + + /* The SSH attribute must be base64 encoded */ + ret = sysdb_attrs_get_el_ext(attrs, SYSDB_SSH_PUBKEY, false, &el); + assert_int_equal(ret, ERR_OK); + assert_int_equal(el->num_values, 1); + decoded_key = sss_base64_decode(test_ctx, + (const char *)el->values[0].data, + &key_len); + assert_non_null(decoded_key); + assert_memory_equal(decoded_key, "1234", key_len); + + /* The extra attribute must not be downloaded, it's not present in map */ + assert_entry_has_no_attr(attrs, "extra"); + + talloc_free(decoded_key); + talloc_free(map); + talloc_free(attrs); +} + +/* Some searches, like rootDSE search do not use any map */ +void test_parse_no_map(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct mock_ldap_entry test_nomap_entry; + struct ldb_message_element *el; + + const char *foo_values[] = { "fooval1", "fooval2", NULL }; + const char *bar_values[] = { "barval1", NULL }; + struct mock_ldap_attr test_nomap_entry_attrs[] = { + { .name = "foo", .values = foo_values }, + { .name = "bar", .values = bar_values }, + { NULL, NULL } + }; + + test_nomap_entry.dn = "cn=testentry,dc=example,dc=com"; + test_nomap_entry.attrs = test_nomap_entry_attrs; + set_entry_parse(&test_nomap_entry); + + ret = sdap_parse_entry(test_ctx, &test_ctx->sh, &test_ctx->sm, + NULL, 0, &attrs, false); + assert_int_equal(ret, ERR_OK); + + assert_int_equal(attrs->num, 3); + assert_entry_has_attr(attrs, SYSDB_ORIG_DN, + "cn=testentry,dc=example,dc=com"); + assert_entry_has_attr(attrs, "bar", "barval1"); + /* Multivalued attributes must return all values */ + ret = sysdb_attrs_get_el_ext(attrs, "foo", false, &el); + assert_int_equal(ret, ERR_OK); + assert_int_equal(el->num_values, 2); + assert_true((strcmp((const char *) el->values[0].data, "fooval1") == 0 && + strcmp((const char *) el->values[1].data, "fooval2") == 0) || + (strcmp((const char *) el->values[1].data, "fooval1") == 0 && + strcmp((const char *) el->values[0].data, "fooval2") == 0)); + + + talloc_free(attrs); +} + +/* Only DN and OC, no real attributes */ +void test_parse_no_attrs(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct mock_ldap_entry test_rfc2307_user; + struct sdap_attr_map *map; + + const char *oc_values[] = { "posixAccount", NULL }; + struct mock_ldap_attr test_rfc2307_user_attrs[] = { + { .name = "objectClass", .values = oc_values }, + { NULL, NULL } + }; + + test_rfc2307_user.dn = "cn=testuser,dc=example,dc=com"; + test_rfc2307_user.attrs = test_rfc2307_user_attrs; + set_entry_parse(&test_rfc2307_user); + + ret = sdap_copy_map(test_ctx, rfc2307_user_map, SDAP_OPTS_USER, &map); + assert_int_equal(ret, ERR_OK); + + ret = sdap_parse_entry(test_ctx, &test_ctx->sh, &test_ctx->sm, + map, SDAP_OPTS_USER, + &attrs, false); + assert_int_equal(ret, ERR_OK); + + assert_int_equal(attrs->num, 1); + assert_entry_has_attr(attrs, SYSDB_ORIG_DN, + "cn=testuser,dc=example,dc=com"); + + talloc_free(map); + talloc_free(attrs); +} + +void test_parse_dups(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct mock_ldap_entry test_dupattr_user; + struct sdap_attr_map *map; + int i; + + const char *oc_values[] = { "posixAccount", NULL }; + const char *uid_values[] = { "1234", NULL }; + struct mock_ldap_attr test_dupattr_attrs[] = { + { .name = "objectClass", .values = oc_values }, + { .name = "idNumber", .values = uid_values }, + { NULL, NULL } + }; + + test_dupattr_user.dn = "cn=dupuser,dc=example,dc=com"; + test_dupattr_user.attrs = test_dupattr_attrs; + set_entry_parse(&test_dupattr_user); + + ret = sdap_copy_map(test_ctx, rfc2307_user_map, SDAP_OPTS_USER, &map); + assert_int_equal(ret, ERR_OK); + /* Set both uidNumber and gidNumber to idNumber */ + for (i = 0; i < SDAP_OPTS_USER; i++) { + if (map[i].name == NULL) continue; + + if (strcmp(map[i].name, "uidNumber") == 0 + || strcmp(map[i].name, "gidNumber") == 0) { + map[i].name = discard_const("idNumber"); + } + } + + ret = sdap_parse_entry(test_ctx, &test_ctx->sh, &test_ctx->sm, + map, SDAP_OPTS_USER, + &attrs, false); + assert_int_equal(ret, ERR_OK); + + assert_int_equal(attrs->num, 3); + + /* Every entry has a DN */ + assert_entry_has_attr(attrs, SYSDB_ORIG_DN, + "cn=dupuser,dc=example,dc=com"); + /* Test the single-valued attribute */ + assert_entry_has_attr(attrs, SYSDB_UIDNUM, "1234"); + assert_entry_has_attr(attrs, SYSDB_GIDNUM, "1234"); + + talloc_free(map); + talloc_free(attrs); +} + +void test_parse_deref(void **state) +{ + errno_t ret; + struct sdap_attr_map_info minfo; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct sdap_deref_attrs **res; + LDAPDerefRes *dref; + + const char *oc_values[] = { "posixAccount", NULL }; + const char *uid_values[] = { "tuser1", NULL }; + const char *extra_values[] = { "extra", NULL }; + struct mock_ldap_attr test_ipa_user_attrs[] = { + { .name = "objectClass", .values = oc_values }, + { .name = "uid", .values = uid_values }, + { .name = "extra", .values = extra_values }, + { NULL, NULL } + }; + struct mock_ldap_entry test_ipa_user; + test_ipa_user.dn = "cn=testuser,dc=example,dc=com"; + test_ipa_user.attrs = test_ipa_user_attrs; + + ret = sdap_copy_map(test_ctx, rfc2307_user_map, SDAP_OPTS_USER, &minfo.map); + minfo.num_attrs = SDAP_OPTS_USER; + assert_int_equal(ret, ERR_OK); + + dref = mock_deref_res(test_ctx, &test_ipa_user); + assert_non_null(dref); + + ret = sdap_parse_deref(test_ctx, &minfo, 1, dref, &res); + talloc_free(dref); + talloc_free(minfo.map); + assert_int_equal(ret, ERR_OK); + assert_non_null(res); + + /* The extra attribute must not be downloaded, it's not present in map */ + assert_non_null(res[0]); + assert_true(res[0]->map == minfo.map); + + assert_entry_has_attr(res[0]->attrs, SYSDB_ORIG_DN, + "cn=testuser,dc=example,dc=com"); + assert_entry_has_attr(res[0]->attrs, SYSDB_NAME, "tuser1"); + assert_entry_has_no_attr(res[0]->attrs, "extra"); + talloc_free(res); +} + +void test_parse_deref_no_attrs(void **state) +{ + errno_t ret; + struct sdap_attr_map_info minfo; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct sdap_deref_attrs **res; + LDAPDerefRes *dref; + + struct mock_ldap_entry test_ipa_user; + test_ipa_user.dn = "cn=testuser,dc=example,dc=com"; + test_ipa_user.attrs = NULL; + + ret = sdap_copy_map(test_ctx, rfc2307_user_map, SDAP_OPTS_USER, &minfo.map); + minfo.num_attrs = SDAP_OPTS_USER; + assert_int_equal(ret, ERR_OK); + + dref = mock_deref_res(test_ctx, &test_ipa_user); + assert_non_null(dref); + + ret = sdap_parse_deref(test_ctx, &minfo, 1, dref, &res); + talloc_free(dref); + talloc_free(minfo.map); + assert_int_equal(ret, ERR_OK); + assert_null(res); /* res must be NULL on receiving no attributes */ +} + +void test_parse_deref_map_mismatch(void **state) +{ + errno_t ret; + struct sdap_attr_map_info minfo; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct sdap_deref_attrs **res; + LDAPDerefRes *dref; + + const char *oc_values[] = { "posixAccount", NULL }; + const char *uid_values[] = { "tuser1", NULL }; + struct mock_ldap_attr test_ipa_user_attrs[] = { + { .name = "objectClass", .values = oc_values }, + { .name = "uid", .values = uid_values }, + { NULL, NULL } + }; + struct mock_ldap_entry test_ipa_user; + test_ipa_user.dn = "cn=testuser,dc=example,dc=com"; + test_ipa_user.attrs = test_ipa_user_attrs; + + ret = sdap_copy_map(test_ctx, rfc2307_group_map, SDAP_OPTS_GROUP, &minfo.map); + minfo.num_attrs = SDAP_OPTS_GROUP; + assert_int_equal(ret, ERR_OK); + + dref = mock_deref_res(test_ctx, &test_ipa_user); + assert_non_null(dref); + + ret = sdap_parse_deref(test_ctx, &minfo, 1, dref, &res); + talloc_free(dref); + talloc_free(minfo.map); + assert_int_equal(ret, ERR_OK); + assert_non_null(res); + /* the group map didn't match, so no attrs will be parsed out of the map */ + assert_null(res[0]->attrs); + talloc_free(res); +} + +void test_parse_secondary_oc(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct mock_ldap_entry test_rfc2307_group; + struct sdap_attr_map *map; + + const char *oc_values[] = { "secondaryOC", NULL }; + const char *uid_values[] = { "tgroup1", NULL }; + struct mock_ldap_attr test_rfc2307_group_attrs[] = { + { .name = "objectClass", .values = oc_values }, + { .name = "uid", .values = uid_values }, + { NULL, NULL } + }; + + test_rfc2307_group.dn = "cn=testgroup,dc=example,dc=com"; + test_rfc2307_group.attrs = test_rfc2307_group_attrs; + set_entry_parse(&test_rfc2307_group); + + ret = sdap_copy_map(test_ctx, rfc2307_group_map, SDAP_OPTS_GROUP, &map); + assert_int_equal(ret, ERR_OK); + map[SDAP_OC_GROUP_ALT].name = discard_const("secondaryOC"); + + ret = sdap_parse_entry(test_ctx, &test_ctx->sh, &test_ctx->sm, + map, SDAP_OPTS_GROUP, + &attrs, false); + assert_int_equal(ret, ERR_OK); + + talloc_free(map); + talloc_free(attrs); +} + +/* Negative test - objectclass doesn't match the map */ +void test_parse_bad_oc(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct mock_ldap_entry test_rfc2307_user; + struct sdap_attr_map *map; + + const char *oc_values[] = { "someRandomValueWhoCaresItsAUnitTest", NULL }; + const char *uid_values[] = { "tuser1", NULL }; + struct mock_ldap_attr test_rfc2307_user_attrs[] = { + { .name = "objectClass", .values = oc_values }, + { .name = "uid", .values = uid_values }, + { NULL, NULL } + }; + + test_rfc2307_user.dn = "cn=testuser,dc=example,dc=com"; + test_rfc2307_user.attrs = test_rfc2307_user_attrs; + set_entry_parse(&test_rfc2307_user); + + ret = sdap_copy_map(test_ctx, rfc2307_user_map, SDAP_OPTS_USER, &map); + assert_int_equal(ret, ERR_OK); + + ret = sdap_parse_entry(test_ctx, &test_ctx->sh, &test_ctx->sm, + map, SDAP_OPTS_USER, + &attrs, false); + assert_int_not_equal(ret, ERR_OK); + + talloc_free(map); +} + +/* Negative test - the entry has no objectClass. Just make sure + * we don't crash + */ +void test_parse_no_oc(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct mock_ldap_entry test_rfc2307_user; + struct sdap_attr_map *map; + + const char *uid_values[] = { "tuser1", NULL }; + struct mock_ldap_attr test_rfc2307_user_attrs[] = { + { .name = "uid", .values = uid_values }, + { NULL, NULL } + }; + + test_rfc2307_user.dn = "cn=testuser,dc=example,dc=com"; + test_rfc2307_user.attrs = test_rfc2307_user_attrs; + set_entry_parse(&test_rfc2307_user); + + ret = sdap_copy_map(test_ctx, rfc2307_user_map, SDAP_OPTS_USER, &map); + assert_int_equal(ret, ERR_OK); + + ret = sdap_parse_entry(test_ctx, &test_ctx->sh, &test_ctx->sm, + map, SDAP_OPTS_USER, + &attrs, false); + assert_int_not_equal(ret, ERR_OK); + + talloc_free(map); +} + +/* Negative test - the entry has no DN. Just make sure + * we don't crash and detect the failure. + */ +void test_parse_no_dn(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct mock_ldap_entry test_rfc2307_user; + struct sdap_attr_map *map; + + const char *oc_values[] = { "posixAccount", NULL }; + const char *uid_values[] = { "tuser1", NULL }; + struct mock_ldap_attr test_rfc2307_user_attrs[] = { + { .name = "objectClass", .values = oc_values }, + { .name = "uid", .values = uid_values }, + { NULL, NULL } + }; + + test_rfc2307_user.dn = NULL; /* Test */ + test_rfc2307_user.attrs = test_rfc2307_user_attrs; + set_entry_parse(&test_rfc2307_user); + + ret = sdap_copy_map(test_ctx, rfc2307_user_map, SDAP_OPTS_USER, &map); + assert_int_equal(ret, ERR_OK); + + ret = sdap_parse_entry(test_ctx, &test_ctx->sh, &test_ctx->sm, + map, SDAP_OPTS_USER, + &attrs, false); + assert_int_not_equal(ret, ERR_OK); + + talloc_free(map); +} + +struct copy_map_entry_test_ctx { + struct sdap_attr_map *src_map; + struct sdap_attr_map *dst_map; +}; + +static int copy_map_entry_test_setup(void **state) +{ + int ret; + struct copy_map_entry_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, + struct copy_map_entry_test_ctx); + assert_non_null(test_ctx); + + ret = sdap_copy_map(test_ctx, rfc2307_user_map, + SDAP_OPTS_USER, &test_ctx->src_map); + assert_int_equal(ret, ERR_OK); + + ret = sdap_copy_map(test_ctx, rfc2307_user_map, + SDAP_OPTS_USER, &test_ctx->dst_map); + assert_int_equal(ret, ERR_OK); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int copy_map_entry_test_teardown(void **state) +{ + struct copy_map_entry_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct copy_map_entry_test_ctx); + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static const char *copy_uuid(struct copy_map_entry_test_ctx *test_ctx) +{ + errno_t ret; + + assert_null(test_ctx->dst_map[SDAP_AT_USER_UUID].name); + ret = sdap_copy_map_entry(test_ctx->src_map, test_ctx->dst_map, + SDAP_AT_USER_UUID); + assert_int_equal(ret, EOK); + return test_ctx->dst_map[SDAP_AT_USER_UUID].name; +} + +static void test_sdap_copy_map_entry(void **state) +{ + struct copy_map_entry_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct copy_map_entry_test_ctx); + const char *uuid_set_val = "test_uuid_val"; + const char *uuid_val = NULL; + + test_ctx->src_map[SDAP_AT_USER_UUID].name = discard_const(uuid_set_val); + + uuid_val = copy_uuid(test_ctx); + assert_non_null(uuid_val); + assert_string_equal(uuid_val, uuid_set_val); + talloc_free(test_ctx->dst_map[SDAP_AT_USER_UUID].name); +} + +static void test_sdap_copy_map_entry_null_name(void **state) +{ + struct copy_map_entry_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct copy_map_entry_test_ctx); + const char *uuid_val = NULL; + + uuid_val = copy_uuid(test_ctx); + assert_null(uuid_val); +} + +struct test_sdap_inherit_ctx { + struct sdap_options *parent_sdap_opts; + struct sdap_options *child_sdap_opts; +}; + +struct sdap_options *mock_sdap_opts(TALLOC_CTX *mem_ctx) +{ + int ret; + struct sdap_options *opts; + + opts = talloc_zero(mem_ctx, struct sdap_options); + assert_non_null(opts); + + ret = sdap_copy_map(opts, rfc2307_user_map, + SDAP_OPTS_USER, &opts->user_map); + assert_int_equal(ret, ERR_OK); + + ret = dp_copy_defaults(opts, default_basic_opts, + SDAP_OPTS_BASIC, &opts->basic); + assert_int_equal(ret, ERR_OK); + + return opts; +} + +static int test_sdap_inherit_option_setup(void **state) +{ + int ret; + struct test_sdap_inherit_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, + struct test_sdap_inherit_ctx); + assert_non_null(test_ctx); + + test_ctx->child_sdap_opts = talloc_zero(test_ctx, struct sdap_options); + + test_ctx->parent_sdap_opts = mock_sdap_opts(test_ctx); + assert_non_null(test_ctx->parent_sdap_opts); + test_ctx->child_sdap_opts = mock_sdap_opts(test_ctx); + assert_non_null(test_ctx->child_sdap_opts); + + test_ctx->parent_sdap_opts->user_map[SDAP_AT_USER_PRINC].name = \ + discard_const("test_princ"); + + ret = dp_opt_set_int(test_ctx->parent_sdap_opts->basic, + SDAP_PURGE_CACHE_TIMEOUT, 123); + assert_int_equal(ret, EOK); + + *state = test_ctx; + return 0; +} + +static int test_sdap_inherit_option_teardown(void **state) +{ + struct test_sdap_inherit_ctx *test_ctx = \ + talloc_get_type_abort(*state, struct test_sdap_inherit_ctx); + + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_sdap_inherit_option_null(void **state) +{ + struct test_sdap_inherit_ctx *test_ctx = \ + talloc_get_type_abort(*state, struct test_sdap_inherit_ctx); + int val; + + val = dp_opt_get_int(test_ctx->child_sdap_opts->basic, + SDAP_PURGE_CACHE_TIMEOUT); + assert_int_equal(val, 0); + + sdap_inherit_options(NULL, + test_ctx->parent_sdap_opts, + test_ctx->child_sdap_opts); + + val = dp_opt_get_int(test_ctx->child_sdap_opts->basic, + SDAP_PURGE_CACHE_TIMEOUT); + assert_int_equal(val, 0); +} + +static void test_sdap_inherit_option_notset(void **state) +{ + struct test_sdap_inherit_ctx *test_ctx = \ + talloc_get_type_abort(*state, struct test_sdap_inherit_ctx); + int val; + const char *inherit_options[] = { "ldap_use_tokengroups", NULL }; + + val = dp_opt_get_int(test_ctx->child_sdap_opts->basic, + SDAP_PURGE_CACHE_TIMEOUT); + assert_int_equal(val, 0); + + /* parent has nondefault, but it's not supposed to be inherited */ + sdap_inherit_options(discard_const(inherit_options), + test_ctx->parent_sdap_opts, + test_ctx->child_sdap_opts); + + val = dp_opt_get_int(test_ctx->child_sdap_opts->basic, + SDAP_PURGE_CACHE_TIMEOUT); + assert_int_equal(val, 0); +} + +static void test_sdap_inherit_option_basic(void **state) +{ + struct test_sdap_inherit_ctx *test_ctx = \ + talloc_get_type_abort(*state, struct test_sdap_inherit_ctx); + int val; + const char *inherit_options[] = { "ldap_purge_cache_timeout", NULL }; + + val = dp_opt_get_int(test_ctx->child_sdap_opts->basic, + SDAP_PURGE_CACHE_TIMEOUT); + assert_int_equal(val, 0); + + /* parent has nondefault, but it's not supposed to be inherited */ + sdap_inherit_options(discard_const(inherit_options), + test_ctx->parent_sdap_opts, + test_ctx->child_sdap_opts); + + val = dp_opt_get_int(test_ctx->child_sdap_opts->basic, + SDAP_PURGE_CACHE_TIMEOUT); + assert_int_equal(val, 123); +} + +static void test_sdap_inherit_option_user(void **state) +{ + struct test_sdap_inherit_ctx *test_ctx = \ + talloc_get_type_abort(*state, struct test_sdap_inherit_ctx); + const char *inherit_options[] = { "ldap_user_principal", NULL }; + + assert_string_equal( + test_ctx->child_sdap_opts->user_map[SDAP_AT_USER_PRINC].name, + "krbPrincipalName"); + + /* parent has nondefault, but it's not supposed to be inherited */ + sdap_inherit_options(discard_const(inherit_options), + test_ctx->parent_sdap_opts, + test_ctx->child_sdap_opts); + + assert_string_equal( + test_ctx->child_sdap_opts->user_map[SDAP_AT_USER_PRINC].name, + "test_princ"); + + talloc_free(test_ctx->child_sdap_opts->user_map[SDAP_AT_USER_PRINC].name); +} + +struct copy_dom_obj_test_ctx { + struct sdap_options *opts; + + struct sss_domain_info *parent; + struct sss_domain_info *child; + + struct sdap_domain *parent_sd; + struct sdap_domain *child_sd; + + struct sysdb_attrs **ldap_objects; + struct sysdb_attrs **dom_objects; +}; + +static struct sysdb_attrs *test_obj(TALLOC_CTX *mem_ctx, + const char *name, + const char *basedn) +{ + errno_t ret; + const char *orig_dn; + struct sysdb_attrs *obj; + + obj = sysdb_new_attrs(mem_ctx); + assert_non_null(obj); + + orig_dn = talloc_asprintf(obj, "CN=%s,%s", name, basedn); + assert_non_null(orig_dn); + + ret = sysdb_attrs_add_string(obj, SYSDB_ORIG_DN, orig_dn); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(obj, SYSDB_NAME, name); + assert_int_equal(ret, EOK); + + return obj; +} + +static struct sdap_domain *create_sdap_domain(struct sdap_options *opts, + struct sss_domain_info *dom) +{ + errno_t ret; + struct sdap_domain *sdom; + struct ldb_context *ldb; + + ret = sdap_domain_add(opts, dom, &sdom); + assert_int_equal(ret, EOK); + + sdom->search_bases = talloc_array(sdom, struct sdap_search_base *, 2); + assert_non_null(sdom->search_bases); + sdom->search_bases[1] = NULL; + + ldb = ldb_init(sdom, NULL); + assert_non_null(ldb); + + ret = sdap_create_search_base(sdom, ldb, sdom->basedn, + LDAP_SCOPE_SUBTREE, + NULL, + &sdom->search_bases[0]); + assert_int_equal(ret, EOK); + + return sdom; +} + +static int sdap_copy_objects_in_dom_setup(void **state) +{ + struct copy_dom_obj_test_ctx *test_ctx; + + test_ctx = talloc_zero(NULL, + struct copy_dom_obj_test_ctx); + assert_non_null(test_ctx); + + test_ctx->opts = talloc_zero(test_ctx, struct sdap_options); + assert_non_null(test_ctx->opts); + + test_ctx->parent = named_domain(test_ctx, "win.trust.test", NULL); + assert_non_null(test_ctx->parent); + + test_ctx->child = named_domain(test_ctx, "child.win.trust.test", + test_ctx->parent); + assert_non_null(test_ctx->child); + + test_ctx->parent_sd = create_sdap_domain(test_ctx->opts, + test_ctx->parent); + assert_non_null(test_ctx->parent_sd); + + test_ctx->child_sd = create_sdap_domain(test_ctx->opts, + test_ctx->child); + assert_non_null(test_ctx->child_sd); + + /* These two objects were 'returned by LDAP' */ + test_ctx->ldap_objects = talloc_zero_array(test_ctx, + struct sysdb_attrs *, 2); + assert_non_null(test_ctx->ldap_objects); + + test_ctx->ldap_objects[0] = test_obj(test_ctx->ldap_objects, "parent", + test_ctx->parent_sd->basedn); + assert_non_null(test_ctx->ldap_objects[0]); + + test_ctx->ldap_objects[1] = test_obj(test_ctx->ldap_objects, "child", + test_ctx->child_sd->basedn); + assert_non_null(test_ctx->ldap_objects[1]); + + /* This is the array we'll filter to */ + test_ctx->dom_objects = talloc_zero_array(test_ctx, + struct sysdb_attrs *, 2); + assert_non_null(test_ctx->dom_objects); + + *state = test_ctx; + return 0; +} + +static int sdap_copy_objects_in_dom_teardown(void **state) +{ + struct copy_dom_obj_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct copy_dom_obj_test_ctx); + + talloc_free(test_ctx); + return 0; +} + +static void test_sdap_copy_objects_in_dom(void **state) +{ + struct copy_dom_obj_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct copy_dom_obj_test_ctx); + size_t count; + + assert_ptr_equal(talloc_parent(test_ctx->ldap_objects[0]), + test_ctx->ldap_objects); + assert_ptr_equal(talloc_parent(test_ctx->ldap_objects[1]), + test_ctx->ldap_objects); + + assert_null(test_ctx->dom_objects[0]); + assert_null(test_ctx->dom_objects[1]); + + count = sdap_steal_objects_in_dom(test_ctx->opts, + test_ctx->dom_objects, + 0, + test_ctx->parent, + test_ctx->ldap_objects, + 2, true); + assert_int_equal(count, 1); + + assert_non_null(test_ctx->dom_objects[0]); + assert_non_null(test_ctx->dom_objects[0] == test_ctx->ldap_objects[0]); + assert_null(test_ctx->dom_objects[1]); + + assert_ptr_equal(talloc_parent(test_ctx->ldap_objects[0]), + test_ctx->dom_objects); + + count = sdap_steal_objects_in_dom(test_ctx->opts, + test_ctx->dom_objects, + 1, + test_ctx->child, + test_ctx->ldap_objects, + 2, true); + assert_int_equal(count, 1); + + assert_non_null(test_ctx->dom_objects[1]); + assert_non_null(test_ctx->dom_objects[1] == test_ctx->ldap_objects[1]); + assert_ptr_equal(talloc_parent(test_ctx->ldap_objects[1]), + test_ctx->dom_objects); +} + +static void test_sdap_copy_objects_in_dom_nofilter(void **state) +{ + struct copy_dom_obj_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct copy_dom_obj_test_ctx); + size_t count; + + count = sdap_steal_objects_in_dom(test_ctx->opts, + test_ctx->dom_objects, + 0, + test_ctx->parent, + test_ctx->ldap_objects, + 2, false); + assert_int_equal(count, 2); + + assert_ptr_equal(talloc_parent(test_ctx->ldap_objects[0]), + test_ctx->dom_objects); + assert_ptr_equal(talloc_parent(test_ctx->ldap_objects[1]), + test_ctx->dom_objects); +} + +#define ORIG_DN "cn=host.example.com+ipHostNumber=192.168.1.1,ou=hosts,dc=example,dc=com" +#define ALT_ORIG_DN "cn=host+ipHostNumber=192.168.1.1,ou=hosts,dc=example,dc=com" +#define ALT2_ORIG_DN "cn=foo+ipHostNumber=192.168.1.1,ou=hosts,dc=example,dc=com" +static void test_sdap_get_primary_name(void **state) +{ + int ret; + const char *out = NULL; + + struct sysdb_attrs *attrs; + struct parse_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct parse_test_ctx); + struct mock_ldap_entry test_nomap_entry; + struct ldb_message_element *el; + struct sdap_attr_map *map; + const char *first_name_in_list = NULL; + + const char *cn_values[] = { "host.example.com", "host", NULL }; + const char *ip_values[] = { "192.168.1.1", NULL }; + + const char *oc_values[] = { "ipHost", NULL }; + struct mock_ldap_attr test_nomap_entry_attrs[] = { + { .name = "objectClass", .values = oc_values }, + { .name = "cn", .values = cn_values }, + { .name = "ipHostNumber", .values = ip_values }, + { NULL, NULL } + }; + + test_nomap_entry.dn = ORIG_DN; + test_nomap_entry.attrs = test_nomap_entry_attrs; + set_entry_parse(&test_nomap_entry); + + ret = sdap_copy_map(test_ctx, iphost_map, SDAP_OPTS_IPHOST, &map); + assert_int_equal(ret, ERR_OK); + + ret = sdap_parse_entry(test_ctx, &test_ctx->sh, &test_ctx->sm, + map, SDAP_OPTS_IPHOST, &attrs, false); + assert_int_equal(ret, ERR_OK); + + assert_int_equal(attrs->num, 3); + assert_entry_has_attr(attrs, SYSDB_ORIG_DN, ORIG_DN); + assert_entry_has_attr(attrs, SYSDB_IP_HOST_ATTR_ADDRESS, "192.168.1.1"); + /* Multivalued attributes must return all values */ + ret = sysdb_attrs_get_el_ext(attrs, SYSDB_NAME, false, &el); + assert_int_equal(ret, ERR_OK); + assert_int_equal(el->num_values, 2); + assert_true((strcmp((const char *) el->values[0].data, "host.example.com") == 0 && + strcmp((const char *) el->values[1].data, "host") == 0) || + (strcmp((const char *) el->values[1].data, "host.example.com") == 0 && + strcmp((const char *) el->values[0].data, "host") == 0)); + first_name_in_list = (const char *) el->values[0].data; + + + ret = sdap_get_primary_name("cn", attrs, &out); + assert_int_equal(ret, EOK); + assert_string_equal(out, "host.example.com"); + + /* check that the first name is returned if there is no matching + * attribute name in the RDN */ + ret = sdap_get_primary_name("foo", attrs, &out); + assert_int_equal(ret, EOK); + assert_string_equal(out, first_name_in_list); + + /* check with the second name value in the RDN */ + sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el); + assert_int_equal(ret, ERR_OK); + assert_int_equal(el->num_values, 1); + assert_string_equal(ORIG_DN, (const char *) el->values[0].data); + talloc_free(el->values[0].data); + el->values[0].data = (uint8_t *) talloc_strdup(el, ALT_ORIG_DN); + assert_non_null(el->values[0].data); + el->values[0].length = sizeof(ALT_ORIG_DN); + + ret = sdap_get_primary_name("cn", attrs, &out); + assert_int_equal(ret, EOK); + assert_string_equal(out, "host"); + + /* check that the first name is returned if there is no matching + * attribute name in the RDN */ + ret = sdap_get_primary_name("foo", attrs, &out); + assert_int_equal(ret, EOK); + assert_string_equal(out, first_name_in_list); + + /* Check with an unexpected name in the DN which is not in the name list */ + sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el); + assert_int_equal(ret, ERR_OK); + assert_int_equal(el->num_values, 1); + assert_string_equal(ALT_ORIG_DN, (const char *) el->values[0].data); + talloc_free(el->values[0].data); + el->values[0].data = (uint8_t *) talloc_strdup(el, ALT2_ORIG_DN); + assert_non_null(el->values[0].data); + el->values[0].length = sizeof(ALT2_ORIG_DN); + + ret = sdap_get_primary_name("cn", attrs, &out); + assert_int_equal(ret, EINVAL); + + talloc_free(map); + talloc_free(attrs); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_parse_with_map, + parse_entry_test_setup, + parse_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_no_map, + parse_entry_test_setup, + parse_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_no_attrs, + parse_entry_test_setup, + parse_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_dups, + parse_entry_test_setup, + parse_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_deref, + parse_entry_test_setup, + parse_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_deref_no_attrs, + parse_entry_test_setup, + parse_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_secondary_oc, + parse_entry_test_setup, + parse_entry_test_teardown), + /* Negative tests */ + cmocka_unit_test_setup_teardown(test_parse_no_oc, + parse_entry_test_setup, + parse_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_bad_oc, + parse_entry_test_setup, + parse_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_no_dn, + parse_entry_test_setup, + parse_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_deref_map_mismatch, + parse_entry_test_setup, + parse_entry_test_teardown), + + /* Map option tests */ + cmocka_unit_test_setup_teardown(test_sdap_copy_map_entry, + copy_map_entry_test_setup, + copy_map_entry_test_teardown), + cmocka_unit_test_setup_teardown(test_sdap_copy_map_entry_null_name, + copy_map_entry_test_setup, + copy_map_entry_test_teardown), + + /* Option inherit tests */ + cmocka_unit_test_setup_teardown(test_sdap_inherit_option_null, + test_sdap_inherit_option_setup, + test_sdap_inherit_option_teardown), + cmocka_unit_test_setup_teardown(test_sdap_inherit_option_notset, + test_sdap_inherit_option_setup, + test_sdap_inherit_option_teardown), + cmocka_unit_test_setup_teardown(test_sdap_inherit_option_basic, + test_sdap_inherit_option_setup, + test_sdap_inherit_option_teardown), + cmocka_unit_test_setup_teardown(test_sdap_inherit_option_user, + test_sdap_inherit_option_setup, + test_sdap_inherit_option_teardown), + + /* Per-domain object filter tests */ + cmocka_unit_test_setup_teardown(test_sdap_copy_objects_in_dom, + sdap_copy_objects_in_dom_setup, + sdap_copy_objects_in_dom_teardown), + cmocka_unit_test_setup_teardown(test_sdap_copy_objects_in_dom_nofilter, + sdap_copy_objects_in_dom_setup, + sdap_copy_objects_in_dom_teardown), + + cmocka_unit_test_setup_teardown(test_sdap_get_primary_name, + parse_entry_test_setup, + parse_entry_test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_sdap_access.c b/src/tests/cmocka/test_sdap_access.c new file mode 100644 index 0000000..9ebb872 --- /dev/null +++ b/src/tests/cmocka/test_sdap_access.c @@ -0,0 +1,264 @@ +/* + Authors: + Pavel Reichl <preichl@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests - sdap access + + 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 <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <setjmp.h> +#include <unistd.h> +#include <sys/types.h> +#include <cmocka.h> +#include <ldb.h> + +#include "tests/common.h" +#include "tests/cmocka/test_expire_common.h" +#include "tests/cmocka/test_sdap_access.h" + +/* linking against function from sdap_access.c module */ +extern bool nds_check_expired(const char *exp_time_str); +extern errno_t sdap_access_rhost(struct ldb_message *user_entry, char *pam_rhost); + +static void nds_check_expired_wrap(void *in, void *_out) +{ + *(bool*)_out = nds_check_expired((const char*)in); +} + +void test_nds_check_expire(void **state) +{ + struct expire_test_ctx *tc; + bool res; + + tc = talloc_get_type(*state, struct expire_test_ctx); + assert_non_null(tc); + + assert_false(nds_check_expired(NULL)); + assert_true(nds_check_expired(tc->invalid_longer_format)); + assert_true(nds_check_expired(tc->invalid_format)); + assert_true(nds_check_expired(tc->past_time)); + assert_false(nds_check_expired(tc->future_time)); + + /* changing time zone has no effect as time of expiration is in UTC */ + expire_test_tz("GST+2", nds_check_expired_wrap, (void*)tc->future_time, + (void*)&res); + assert_false(res); + expire_test_tz("GST-2", nds_check_expired_wrap, (void*)tc->future_time, + (void*)&res); + assert_false(res); +} + +static int test_sdap_access_rhost_setup(void **state) +{ + TALLOC_CTX *mem_ctx; + struct test_sdap_access_rhost_ctx *test_ctx; + struct ldb_message *user_no_rhost; + struct ldb_message *user_allow_somehost; + struct ldb_message *user_deny_somehost; + struct ldb_message *user_allow_all; + struct ldb_message *user_allow_all_deny_somehost; + struct ldb_message *user_allow_all_allow_somehost_deny_somehost; + + mem_ctx = talloc_new(NULL); + assert_non_null(mem_ctx); + + test_ctx = talloc(mem_ctx, struct test_sdap_access_rhost_ctx); + assert_non_null(test_ctx); + + /* Setup empty user entry (with 0 entries for rhost) */ + user_no_rhost = ldb_msg_new(test_ctx); + assert_non_null(user_no_rhost); + user_no_rhost->num_elements = 0; + + /* Setup user entry with allow somehost */ + user_allow_somehost = ldb_msg_new(test_ctx); + assert_non_null(user_allow_somehost); + ldb_msg_add_string(user_allow_somehost, + SYSDB_AUTHORIZED_RHOST, + "somehost"); + + /* Setup user entry with deny somehost */ + user_deny_somehost = ldb_msg_new(test_ctx); + assert_non_null(user_deny_somehost); + ldb_msg_add_string(user_deny_somehost, + SYSDB_AUTHORIZED_RHOST, + "!somehost"); + + /* Setup user entry with allow all */ + user_allow_all = ldb_msg_new(test_ctx); + assert_non_null(user_allow_all); + ldb_msg_add_string(user_allow_all, + SYSDB_AUTHORIZED_RHOST, + "*"); + + /* Setup user entry with allow all and deny somehost */ + user_allow_all_deny_somehost = ldb_msg_new(test_ctx); + assert_non_null(user_allow_all_deny_somehost); + ldb_msg_add_string(user_allow_all_deny_somehost, + SYSDB_AUTHORIZED_RHOST, + "*"); + ldb_msg_add_string(user_allow_all_deny_somehost, + SYSDB_AUTHORIZED_RHOST, + "!somehost"); + + /* Setup user entry with allow all, allow somehost and deny somehost */ + user_allow_all_allow_somehost_deny_somehost = ldb_msg_new(test_ctx); + assert_non_null(user_allow_all_allow_somehost_deny_somehost); + ldb_msg_add_string(user_allow_all_allow_somehost_deny_somehost, + SYSDB_AUTHORIZED_RHOST, + "*"); + ldb_msg_add_string(user_allow_all_allow_somehost_deny_somehost, + SYSDB_AUTHORIZED_RHOST, + "!somehost"); + ldb_msg_add_string(user_allow_all_allow_somehost_deny_somehost, + SYSDB_AUTHORIZED_RHOST, + "somehost"); + + /* Setup test context */ + test_ctx->user_no_rhost = user_no_rhost; + test_ctx->user_allow_somehost = user_allow_somehost; + test_ctx->user_deny_somehost = user_deny_somehost; + test_ctx->user_allow_all = user_allow_all; + test_ctx->user_allow_all_deny_somehost = user_allow_all_deny_somehost; + test_ctx->user_allow_all_allow_somehost_deny_somehost = \ + user_allow_all_allow_somehost_deny_somehost; + + *state = test_ctx; + + return 0; +} + +static int test_sdap_access_rhost_teardown(void **state) +{ + struct test_sdap_access_rhost_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct test_sdap_access_rhost_ctx); + assert_non_null(test_ctx); + + talloc_free(test_ctx); + + return 0; +} + +static void test_sdap_access_rhost(void **state) +{ + struct test_sdap_access_rhost_ctx *test_ctx; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct test_sdap_access_rhost_ctx); + assert_non_null(test_ctx); + + char pam_rhost_mock_empty[] = ""; + char pam_rhost_mock_somehost[] = "somehost"; + char pam_rhost_mock_someotherhost[] = "someotherhost"; + + /* Test both arguments as NULL */ + ret = sdap_access_rhost(NULL, NULL); + assert_int_equal(ERR_ACCESS_DENIED, ret); /* Expected access granted */ + + /* Test with user_entry == NULL and rhost == "somehost" */ + ret = sdap_access_rhost(NULL, pam_rhost_mock_somehost); + assert_int_equal(ERR_ACCESS_DENIED, ret); /* Expected access denied */ + + /* Test with user_no_rhost and rhost == NULL */ + ret = sdap_access_rhost(test_ctx->user_no_rhost, NULL); + assert_int_equal(EOK, ret); /* Expected access granted */ + + /* Test with user_no_rhost and rhost == "" (local access) */ + ret = sdap_access_rhost(test_ctx->user_no_rhost, pam_rhost_mock_empty); + assert_int_equal(EOK, ret); /* Expected access granted */ + + /* Test with user_no_rhost and rhost == "somehost" */ + ret = sdap_access_rhost(test_ctx->user_no_rhost, pam_rhost_mock_somehost); + assert_int_equal(ERR_ACCESS_DENIED, ret); /* Expected access denied */ + + /* Test with user_allow_somehost and rhost == "somehost" */ + ret = sdap_access_rhost(test_ctx->user_allow_somehost, + pam_rhost_mock_somehost); + assert_int_equal(EOK, ret); /* Expected access allowed */ + + /* Test with user_deny_somehost and rhost == "somehost" */ + ret = sdap_access_rhost(test_ctx->user_deny_somehost, + pam_rhost_mock_somehost); + assert_int_equal(ERR_ACCESS_DENIED, ret); /* Expected access denied */ + + /* Test with user_allow_all and rhost == "somehost" */ + ret = sdap_access_rhost(test_ctx->user_allow_all, + pam_rhost_mock_somehost); + assert_int_equal(EOK, ret); /* Expected access allowed */ + + /* Test with user_allow_all_deny_somehost and rhost == "somehost" */ + ret = sdap_access_rhost(test_ctx->user_allow_all_deny_somehost, + pam_rhost_mock_somehost); + assert_int_equal(ERR_ACCESS_DENIED, ret); /* Expected access denied */ + + /* Test with user_allow_all_allow_somehost_deny_somehost + * and rhost == "somehost" */ + ret = sdap_access_rhost( + test_ctx->user_allow_all_allow_somehost_deny_somehost, + pam_rhost_mock_somehost); + assert_int_equal(ERR_ACCESS_DENIED, ret); /* Expected access denied */ + + /* Test with user_no_rhost and rhost == "someotherhost" */ + ret = sdap_access_rhost(test_ctx->user_no_rhost, + pam_rhost_mock_someotherhost); + assert_int_equal(ERR_ACCESS_DENIED, ret); /* Expected access denied */ + + /* Test with user_allow_somehost and rhost == "someotherhost" */ + ret = sdap_access_rhost(test_ctx->user_allow_somehost, + pam_rhost_mock_someotherhost); + assert_int_equal(ERR_ACCESS_DENIED, ret); /* Expected access denied */ + + /* Test with user_deny_somehost and rhost == "someotherhost" */ + ret = sdap_access_rhost(test_ctx->user_deny_somehost, + pam_rhost_mock_someotherhost); + assert_int_equal(ERR_ACCESS_DENIED, ret); /* Expected access denied */ + + /* Test with user_allow_all and rhost == "someotherhost" */ + ret = sdap_access_rhost(test_ctx->user_allow_all, + pam_rhost_mock_someotherhost); + assert_int_equal(EOK, ret); /* Expected access allowed */ + + /* Test with user_allow_all_deny_somehost and rhost == "someotherhost" */ + ret = sdap_access_rhost(test_ctx->user_allow_all_deny_somehost, + pam_rhost_mock_someotherhost); + assert_int_equal(EOK, ret); /* Expected access allowed */ + + /* Test with user_allow_all_allow_somehost_deny_somehost + * and rhost == "someotherhost" */ + ret = sdap_access_rhost( + test_ctx->user_allow_all_allow_somehost_deny_somehost, + pam_rhost_mock_someotherhost); + assert_int_equal(EOK, ret); /* Expected access allowed */ +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nds_check_expire, + expire_test_setup, + expire_test_teardown), + cmocka_unit_test_setup_teardown(test_sdap_access_rhost, + test_sdap_access_rhost_setup, + test_sdap_access_rhost_teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_sdap_access.h b/src/tests/cmocka/test_sdap_access.h new file mode 100644 index 0000000..cc49a82 --- /dev/null +++ b/src/tests/cmocka/test_sdap_access.h @@ -0,0 +1,36 @@ +/* + Authors: + Alexey Kamenskiy <aleksey.kamensky@gmail.com> + + SSSD tests - sdap access tests + + 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 TEST_SDAP_ACCESS_H +#define TEST_SDAP_ACCESS_H + +struct test_sdap_access_rhost_ctx { + struct ldb_message *user_no_rhost; + struct ldb_message *user_allow_somehost; + struct ldb_message *user_deny_somehost; + struct ldb_message *user_allow_all; + struct ldb_message *user_allow_all_deny_somehost; + struct ldb_message *user_allow_all_allow_somehost_deny_somehost; +}; + +static int test_sdap_access_rhost_setup(void **state); +static int test_sdap_access_rhost_teardown(void **state); + +#endif /* TEST_SDAP_ACCESS_H */ diff --git a/src/tests/cmocka/test_sdap_certmap.c b/src/tests/cmocka/test_sdap_certmap.c new file mode 100644 index 0000000..aeb924e --- /dev/null +++ b/src/tests/cmocka/test_sdap_certmap.c @@ -0,0 +1,244 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2017 Red Hat + + SSSD tests - sdap certmap + + 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 <stdarg.h> +#include <stdbool.h> +#include <setjmp.h> +#include <unistd.h> +#include <cmocka.h> +#include <popt.h> + +#include "providers/ldap/ldap_common.h" +#include "tests/common.h" +#include "db/sysdb.h" + +#define TESTS_PATH "certmap_" BASE_FILE_STEM +#define TEST_CONF_DB "test_sysdb_certmap.ldb" +#define TEST_ID_PROVIDER "ldap" +#define TEST_DOM_NAME "certmap_test" + +struct certmap_info map_a = { discard_const("map_a"), 11, + NULL, discard_const("(abc=def)"), + NULL }; +struct certmap_info map_b = { discard_const("map_b"), UINT_MAX, + NULL, NULL, NULL }; +struct certmap_info *certmap[] = { &map_a, &map_b, NULL }; + +struct certmap_test_ctx { + struct sss_test_ctx *tctx; + struct sdap_id_ctx *id_ctx; +}; + +static int test_sysdb_setup(void **state) +{ + int ret; + struct certmap_test_ctx *test_ctx; + struct sss_test_conf_param params[] = { + { NULL, NULL }, /* Sentinel */ + }; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, + struct certmap_test_ctx); + assert_non_null(test_ctx); + check_leaks_push(test_ctx); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, params); + assert_non_null(test_ctx->tctx); + + ret = sysdb_update_certmap(test_ctx->tctx->sysdb, certmap, false); + assert_int_equal(ret, EOK); + + test_ctx->id_ctx = talloc_zero(test_ctx->tctx, struct sdap_id_ctx); + assert_non_null(test_ctx->id_ctx); + + test_ctx->id_ctx->opts = talloc_zero(test_ctx->tctx, struct sdap_options); + assert_non_null(test_ctx->id_ctx->opts); + + test_ctx->id_ctx->be = talloc_zero(test_ctx->tctx, struct be_ctx); + assert_non_null(test_ctx->id_ctx->be); + test_ctx->id_ctx->be->domain = test_ctx->tctx->dom; + + *state = test_ctx; + return 0; +} + +static int test_sysdb_teardown(void **state) +{ + struct certmap_test_ctx *test_ctx = + talloc_get_type(*state, struct certmap_test_ctx); + + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + talloc_free(test_ctx->tctx); + assert_true(check_leaks_pop(test_ctx)); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_sdap_certmap_init(void **state) +{ + int ret; + struct certmap_test_ctx *test_ctx = talloc_get_type(*state, + struct certmap_test_ctx); + + ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); + assert_int_equal(ret, EOK); + + talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx); +} + +static void test_sdap_get_sss_certmap(void **state) +{ + int ret; + struct certmap_test_ctx *test_ctx = talloc_get_type(*state, + struct certmap_test_ctx); + struct sss_certmap_ctx *sss_certmap_ctx; + + sss_certmap_ctx = sdap_get_sss_certmap(NULL); + assert_null(sss_certmap_ctx); + + ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); + assert_int_equal(ret, EOK); + + sss_certmap_ctx = sdap_get_sss_certmap( + test_ctx->id_ctx->opts->sdap_certmap_ctx); + assert_non_null(sss_certmap_ctx); + + talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx); +} + +static void test_sdap_certmap_init_twice(void **state) +{ + int ret; + struct certmap_test_ctx *test_ctx = talloc_get_type(*state, + struct certmap_test_ctx); + struct sdap_certmap_ctx *sdap_certmap_ref; + struct sss_certmap_ctx *sss_certmap_ref; + + ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); + assert_int_equal(ret, EOK); + + sdap_certmap_ref = test_ctx->id_ctx->opts->sdap_certmap_ctx; + sss_certmap_ref = sdap_get_sss_certmap(sdap_certmap_ref); + + ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); + assert_int_equal(ret, EOK); + + assert_ptr_equal(sdap_certmap_ref, + test_ctx->id_ctx->opts->sdap_certmap_ctx); + assert_ptr_not_equal(sss_certmap_ref, + sdap_get_sss_certmap(sdap_certmap_ref)); + + talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx); +} + + +static void test_sdap_setup_certmap(void **state) +{ + int ret; + struct certmap_test_ctx *test_ctx = talloc_get_type(*state, + struct certmap_test_ctx); + struct sdap_certmap_ctx *sdap_certmap_ref; + struct sss_certmap_ctx *sss_certmap_ref; + + ret = sdap_init_certmap(test_ctx, test_ctx->id_ctx); + assert_int_equal(ret, EOK); + + sdap_certmap_ref = test_ctx->id_ctx->opts->sdap_certmap_ctx; + sss_certmap_ref = sdap_get_sss_certmap(sdap_certmap_ref); + + ret = sdap_setup_certmap(NULL, NULL); + assert_int_equal(ret, EINVAL); + assert_ptr_equal(sdap_certmap_ref, + test_ctx->id_ctx->opts->sdap_certmap_ctx); + assert_ptr_equal(sss_certmap_ref, sdap_get_sss_certmap(sdap_certmap_ref)); + + ret = sdap_setup_certmap(NULL, certmap); + assert_int_equal(ret, EINVAL); + assert_ptr_equal(sdap_certmap_ref, + test_ctx->id_ctx->opts->sdap_certmap_ctx); + assert_ptr_equal(sss_certmap_ref, sdap_get_sss_certmap(sdap_certmap_ref)); + + ret = sdap_setup_certmap(sdap_certmap_ref, certmap); + assert_int_equal(ret, EOK); + assert_ptr_equal(sdap_certmap_ref, + test_ctx->id_ctx->opts->sdap_certmap_ctx); + assert_ptr_not_equal(sss_certmap_ref, + sdap_get_sss_certmap(sdap_certmap_ref)); + + talloc_free(test_ctx->id_ctx->opts->sdap_certmap_ctx); +} + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sdap_certmap_init, + test_sysdb_setup, + test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sdap_get_sss_certmap, + test_sysdb_setup, + test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sdap_certmap_init_twice, + test_sysdb_setup, + test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sdap_setup_certmap, + test_sysdb_setup, + test_sysdb_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/cmocka/test_sdap_initgr.c b/src/tests/cmocka/test_sdap_initgr.c new file mode 100644 index 0000000..b30b63d --- /dev/null +++ b/src/tests/cmocka/test_sdap_initgr.c @@ -0,0 +1,542 @@ +/* + Authors: + Petr ト憩ch <pcech@redhat.com> + + Copyright (C) 2017 Red Hat + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <stdarg.h> +#include <stdlib.h> +#include <pwd.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_sysdb_objects.h" +#include "tests/cmocka/common_mock_sdap.h" +#include "providers/ad/ad_common.h" + +#include "providers/ad/ad_opts.c" +#include "providers/ldap/sdap_async_initgroups.c" + +/* Declarations from providers/ldap/sdap_async_initgroups.c */ +struct sdap_get_initgr_state; +static int sdap_search_initgr_user_in_batch(struct sdap_get_initgr_state *state, + struct sysdb_attrs **users, + size_t count); + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_sdap_initgr_conf.ldb" +#define TEST_ID_PROVIDER "ldap" + +#define TEST_DOM1_NAME "domain.test.com" +#define TEST_DOM2_NAME "subdom1.domain.test.com" +#define TEST_DOM3_NAME "another_domain.test.com" + +#define OBJECT_BASE_DN1 "dc=domain,dc=test,dc=com,cn=sysdb" +#define OBJECT_BASE_DN2 "dc=subdom1,dc=domain,dc=test,dc=com,cn=sysdb" +#define OBJECT_BASE_DN3 "dc=another_domain,dc=test,dc=com,cn=sysdb" + +#define TEST_USER_1 "test_user_1" +#define TEST_USER_2 "test_user_2" +#define TEST_USER_3 "test_user_3" + +const char *domains[] = { TEST_DOM1_NAME, + TEST_DOM2_NAME, + TEST_DOM3_NAME, + NULL }; + +const char *object_bases[] = { OBJECT_BASE_DN1, + OBJECT_BASE_DN2, + OBJECT_BASE_DN3, + NULL }; + +const char *test_users[] = { TEST_USER_1, + TEST_USER_2, + TEST_USER_3, + NULL }; + +/* ====================== Utilities =============================== */ + +struct test_sdap_initgr_ctx { + struct sss_test_ctx *tctx; +}; + +static struct passwd **get_users(TALLOC_CTX *ctx) +{ + struct passwd **passwds = NULL; + char *homedir = NULL; + size_t user_count = 0; + + for (int i = 0; test_users[i] != NULL; i++) { + user_count++; + } + passwds = talloc_array(ctx, struct passwd *, user_count); + assert_non_null(passwds); + + for (int i = 0; i < user_count; i++) { + passwds[i] = talloc(passwds, struct passwd); + assert_non_null(passwds[i]); + + homedir = talloc_strdup_append(homedir, "/home/"); + homedir = talloc_strdup_append(homedir, test_users[i]); + + passwds[i]->pw_name = discard_const(test_users[i]); + passwds[i]->pw_uid = 567 + i; + passwds[i]->pw_gid = 890 + i; + passwds[i]->pw_dir = talloc_strdup(passwds[i], homedir); + passwds[i]->pw_gecos = discard_const(test_users[i]); + passwds[i]->pw_shell = discard_const("/bin/sh"); + passwds[i]->pw_passwd = discard_const("*"); + + talloc_zfree(homedir); + } + + return passwds; +} + +static struct sss_test_conf_param **get_params(TALLOC_CTX *ctx) +{ + struct sss_test_conf_param **params; + char *user_base_dn = NULL; + char *group_base_dn = NULL; + size_t base_count = 0; + + for (int i = 0; object_bases[i] != NULL; i++) { + base_count++; + } + + params = talloc_array(ctx, struct sss_test_conf_param *, base_count + 1); + assert_non_null(params); + + for (int i = 0; i < base_count; i++) { + params[i] = talloc(params, struct sss_test_conf_param); + assert_non_null(params[i]); + + user_base_dn = talloc_strdup_append(user_base_dn, "cn=users,"); + user_base_dn = talloc_strdup_append(user_base_dn, object_bases[i]); + + group_base_dn = talloc_strdup_append(group_base_dn, "cn=groups,"); + group_base_dn = talloc_strdup_append(group_base_dn, object_bases[i]); + + params[i] = talloc_array(params[i], struct sss_test_conf_param, 5); + params[i][0].key = "ldap_schema"; + params[i][0].value = "rfc2307bis"; + params[i][1].key = "ldap_search_base"; + params[i][1].value = talloc_strdup(params[i], object_bases[i]); + params[i][2].key = "ldap_user_search_base"; + params[i][2].value = talloc_strdup(params[i], user_base_dn); + params[i][3].key = "ldap_group_search_base"; + params[i][3].value = talloc_strdup(params[i], group_base_dn); + params[i][4].key = NULL; + params[i][4].value = NULL; + + talloc_zfree(user_base_dn); + talloc_zfree(group_base_dn); + } + + return params; +} + +struct sss_domain_info *get_domain_info(struct sss_domain_info *domain, + const char *domain_name) +{ + struct sss_domain_info *dom = domain; + + while(dom != NULL) { + if (strcmp(dom->name, domain_name) == 0) { + break; + } + dom = dom->next; + } + + return dom; +} + +struct sdap_get_initgr_state *prepare_state(struct test_sdap_initgr_ctx *ctx, + const char **domain_names) +{ + struct sdap_get_initgr_state *state; + struct sss_domain_info *dom_info = NULL; + struct sss_domain_info *recent_dom_info = NULL; + + state = talloc_zero(ctx->tctx, struct sdap_get_initgr_state); + assert_non_null(state); + + for (int i=0; domain_names[i] != NULL; i++) { + dom_info = get_domain_info(ctx->tctx->dom, domain_names[i]); + assert_non_null(dom_info); + + if (i == 0) { + state->dom = dom_info; + recent_dom_info = state->dom; + } else { + recent_dom_info->next = dom_info; + recent_dom_info = recent_dom_info->next; + } + } + assert_non_null(state->dom); + assert_non_null(recent_dom_info); + recent_dom_info->next = NULL; + + state->opts = mock_sdap_options_ldap(state, state->dom, + ctx->tctx->confdb, + ctx->tctx->conf_dom_path); + assert_non_null(state->opts); + + return state; +} + +/* TODO: This function is copied from test_nss_srv.c + * It could be fine move both to one place, + * for example src/tests/common_sysdb.c + */ +static errno_t store_user(TALLOC_CTX *ctx, + struct sss_domain_info *dom, + struct passwd *user, + struct sysdb_attrs *attrs, + time_t cache_update) +{ + errno_t ret; + char *fqname; + + fqname = sss_create_internal_fqname(ctx, + user->pw_name, + dom->name); + if (fqname == NULL) { + return ENOMEM; + } + + /* Prime the cache with a valid user */ + ret = sysdb_store_user(dom, + fqname, + user->pw_passwd, + user->pw_uid, + user->pw_gid, + user->pw_gecos, + user->pw_dir, + user->pw_shell, + NULL, attrs, + NULL, 300, cache_update); + talloc_free(fqname); + + return ret; +} + +/* ====================== Setup =============================== */ + +static int test_sdap_initgr_setup_one_domain(void **state) +{ + struct test_sdap_initgr_ctx *test_ctx; + struct sss_test_conf_param **params; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_sdap_initgr_ctx); + assert_non_null(test_ctx); + + params = get_params(test_ctx); + assert_non_null(params); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains[0], + TEST_ID_PROVIDER, params[0]); + assert_non_null(test_ctx->tctx); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int test_sdap_initgr_setup_multi_domains(void **state) +{ + struct test_sdap_initgr_ctx *test_ctx; + struct sss_test_conf_param **params; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_sdap_initgr_ctx); + assert_non_null(test_ctx); + + params = get_params(test_ctx); + assert_non_null(params); + + test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains, + TEST_ID_PROVIDER, params); + assert_non_null(test_ctx->tctx); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int test_sdap_initgr_setup_other_multi_domains(void **state) +{ + struct test_sdap_initgr_ctx *test_ctx; + struct sss_test_conf_param **params; + const char *domains_vith_other[] = { TEST_DOM1_NAME, + TEST_DOM3_NAME, + NULL }; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_sdap_initgr_ctx); + assert_non_null(test_ctx); + + params = get_params(test_ctx); + assert_non_null(params); + + test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains_vith_other, + TEST_ID_PROVIDER, params); + assert_non_null(test_ctx->tctx); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int test_sdap_initgr_teardown(void **state) +{ + struct test_sdap_initgr_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct test_sdap_initgr_ctx); + assert_non_null(test_ctx); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +/* ====================== The tests =============================== */ + +static void test_user_is_on_batch(void **state) +{ + struct test_sdap_initgr_ctx *test_ctx; + struct sdap_get_initgr_state *initgr_state; + const char *domains_set[] = { domains[0], NULL }; + struct sss_domain_info *dom1_info = NULL; + struct sss_domain_info *dom2_info = NULL; + struct passwd **passwd_users; + struct sysdb_attrs **users; + const char *user_name; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct test_sdap_initgr_ctx); + assert_non_null(test_ctx); + + dom1_info = get_domain_info(test_ctx->tctx->dom, domains[0]); + assert_non_null(dom1_info); + dom2_info = get_domain_info(test_ctx->tctx->dom, domains[1]); + assert_non_null(dom2_info); + + initgr_state = prepare_state(test_ctx, domains_set); + assert_non_null(initgr_state); + + passwd_users = get_users(test_ctx); + assert_non_null(passwd_users); + + ret = store_user(test_ctx, dom1_info, passwd_users[0], NULL, 0); + assert_int_equal(ret, 0); + ret = store_user(test_ctx, dom2_info, passwd_users[1], NULL, 0); + assert_int_equal(ret, 0); + + users = talloc_array(test_ctx, struct sysdb_attrs *, 2); + users[0] = mock_sysdb_user(users, object_bases[0], + passwd_users[0]->pw_uid, + passwd_users[0]->pw_name); + users[1] = mock_sysdb_user(users, object_bases[1], + passwd_users[1]->pw_uid, + passwd_users[1]->pw_name); + + ret = sdap_search_initgr_user_in_batch(initgr_state, users, 2); + assert_int_equal(ret, 0); + + ret = sysdb_attrs_get_string(initgr_state->orig_user, "name", &user_name); + assert_int_equal(ret, 0); + assert_string_equal(user_name, passwd_users[0]->pw_name); + + talloc_zfree(initgr_state); + talloc_zfree(passwd_users); + talloc_zfree(users); +} + +static void test_user_is_from_subdomain(void **state) +{ + struct test_sdap_initgr_ctx *test_ctx; + struct sdap_get_initgr_state *initgr_state; + const char *domains_set[] = { domains[0], NULL }; + struct sss_domain_info *dom_info = NULL; + struct passwd **passwd_users; + struct sysdb_attrs **users; + const char *user_name; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct test_sdap_initgr_ctx); + assert_non_null(test_ctx); + + dom_info = get_domain_info(test_ctx->tctx->dom, domains[0]); + assert_non_null(dom_info); + + initgr_state = prepare_state(test_ctx, domains_set); + assert_non_null(initgr_state); + + passwd_users = get_users(test_ctx); + assert_non_null(passwd_users); + + ret = store_user(test_ctx, dom_info, passwd_users[0], NULL, 0); + assert_int_equal(ret, 0); + + users = talloc_array(test_ctx, struct sysdb_attrs *, 1); + users[0] = mock_sysdb_user(users, object_bases[1], + passwd_users[1]->pw_uid, + passwd_users[1]->pw_name); + + const char *original_dn = NULL; + ret = sysdb_attrs_get_string(users[0], SYSDB_ORIG_DN, &original_dn); + + ret = sdap_search_initgr_user_in_batch(initgr_state, users, 1); + assert_int_equal(ret, 0); + + ret = sysdb_attrs_get_string(initgr_state->orig_user, "name", &user_name); + assert_int_equal(ret, 0); + assert_string_equal(user_name, passwd_users[1]->pw_name); + + talloc_zfree(initgr_state); + talloc_zfree(passwd_users); + talloc_zfree(users); +} + +static void test_user_is_from_another_domain(void **state) +{ + struct test_sdap_initgr_ctx *test_ctx; + struct sdap_get_initgr_state *initgr_state; + const char *domains_set[] = { domains[0], domains[2], NULL }; + struct sss_domain_info *dom_info = NULL; + struct sss_domain_info *other_dom_info = NULL; + struct sdap_domain *other_sdom = NULL; + struct passwd **passwd_users; + struct sysdb_attrs **users; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct test_sdap_initgr_ctx); + assert_non_null(test_ctx); + + dom_info = get_domain_info(test_ctx->tctx->dom, domains[0]); + assert_non_null(dom_info); + + initgr_state = prepare_state(test_ctx, domains_set); + assert_non_null(initgr_state); + + other_dom_info = get_domain_info(test_ctx->tctx->dom, domains[2]); + assert_non_null(other_dom_info); + + ret = sdap_domain_add(initgr_state->opts, other_dom_info, &other_sdom); + assert_int_equal(ret, EOK); + + talloc_zfree(other_sdom->search_bases); + other_sdom->search_bases = talloc_array(other_sdom, + struct sdap_search_base *, 2); + assert_non_null(other_sdom->search_bases); + other_sdom->search_bases[1] = NULL; + + ret = sdap_create_search_base(other_sdom, + sysdb_ctx_get_ldb(dom_info->sysdb), + object_bases[2], + LDAP_SCOPE_SUBTREE, NULL, + &other_sdom->search_bases[0]); + assert_int_equal(ret, EOK); + + passwd_users = get_users(test_ctx); + assert_non_null(passwd_users); + + ret = store_user(test_ctx, dom_info, passwd_users[0], NULL, 0); + assert_int_equal(ret, 0); + + users = talloc_array(test_ctx, struct sysdb_attrs *, 1); + users[0] = mock_sysdb_user(users, object_bases[2], + passwd_users[2]->pw_uid, + passwd_users[2]->pw_name); + + ret = sdap_search_initgr_user_in_batch(initgr_state, users, 1); + assert_int_equal(ret, EINVAL); + + talloc_zfree(initgr_state); + talloc_zfree(passwd_users); + talloc_zfree(users); +} + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_user_is_on_batch, + test_sdap_initgr_setup_multi_domains, + test_sdap_initgr_teardown), + cmocka_unit_test_setup_teardown(test_user_is_from_subdomain, + test_sdap_initgr_setup_one_domain, + test_sdap_initgr_teardown), + cmocka_unit_test_setup_teardown(test_user_is_from_another_domain, + test_sdap_initgr_setup_other_multi_domains, + test_sdap_initgr_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + + test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0) { + test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); + } + + return rv; +} diff --git a/src/tests/cmocka/test_search_bases.c b/src/tests/cmocka/test_search_bases.c new file mode 100644 index 0000000..8deed6f --- /dev/null +++ b/src/tests/cmocka/test_search_bases.c @@ -0,0 +1,281 @@ +/* + Authors: + Pavel Reichl <preichl@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests - Search bases + + 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 <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <setjmp.h> +#include <unistd.h> +#include <sys/types.h> +#include <cmocka.h> +#include <ldap.h> + +#include "util/find_uid.h" +#include "util/sss_ldap.h" +#include "tests/common.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap.h" +#include "dhash.h" + +enum sss_test_get_by_dn { + DN_NOT_IN_DOMS, /* dn is not in any domain */ + DN_IN_DOM1, /* dn is in the domain based on dns */ + DN_IN_DOM2, /* dn is in the domain based on dns2 */ +}; + +struct test_ctx { + struct ldb_context *ldb; +}; + +static int test_setup(void **state) +{ + struct test_ctx *test_ctx; + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + + test_ctx->ldb = ldb_init(test_ctx, NULL); + assert_non_null(test_ctx->ldb); + + *state = test_ctx; + return 0; +} + +static int test_teardown(void **state) +{ + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + + talloc_free(test_ctx); + return 0; +} + +static struct sdap_search_base** generate_bases(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, + const char** dns, size_t n) +{ + struct sdap_search_base **search_bases; + errno_t err; + int i; + + search_bases = talloc_array(mem_ctx, struct sdap_search_base *, n + 1); + assert_non_null(search_bases); + + for (i=0; i < n; ++i) { + err = sdap_create_search_base(mem_ctx, ldb, dns[i], LDAP_SCOPE_SUBTREE, + NULL, &search_bases[i]); + if (err != EOK) { + fprintf(stderr, "Failed to create search base\n"); + } + assert_int_equal(err, EOK); + } + search_bases[n] = NULL; + return search_bases; +} + +static bool do_test_search_bases(struct test_ctx *test_ctx, const char* dn, + const char** dns, size_t n) +{ + TALLOC_CTX *tmp_ctx; + struct sdap_search_base **search_bases; + bool ret; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + + search_bases = generate_bases(tmp_ctx, test_ctx->ldb, dns, n); + check_leaks_push(tmp_ctx); + ret = sss_ldap_dn_in_search_bases(tmp_ctx, dn, search_bases, NULL); + assert_true(check_leaks_pop(tmp_ctx) == true); + + talloc_free(tmp_ctx); + return ret; +} + +void test_search_bases_fail(void **state) +{ + const char *dn = "cn=user, dc=sub, dc=ad, dc=pb"; + const char *dns[] = {"dc=example, dc=com", "dc=subdom, dc=ad, dc=pb"}; + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + bool ret; + + ret = do_test_search_bases(test_ctx, dn, dns, 2); + assert_false(ret); +} + +void test_search_bases_success(void **state) +{ + const char *dn = "cn=user, dc=sub, dc=ad, dc=pb"; + const char *dns[] = {"", "dc=ad, dc=pb", "dc=sub, dc=ad, dc=pb"}; + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + bool ret; + + ret = do_test_search_bases(test_ctx, dn, dns, 3); + assert_true(ret); +} + +static void do_test_get_by_dn(struct test_ctx *test_ctx, const char *dn, + const char **dns, size_t n, + const char **dns2, size_t n2, int expected_result) +{ + TALLOC_CTX *tmp_ctx; + struct sdap_options *opts; + struct sdap_domain *sdom; + struct sdap_domain *sdom2; + struct sdap_domain *res_sdom; + struct sdap_search_base **search_bases; + struct sdap_search_base **search_bases2; + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + + search_bases = generate_bases(tmp_ctx, test_ctx->ldb, dns, n); + search_bases2 = generate_bases(tmp_ctx, test_ctx->ldb, dns2, n2); + sdom = talloc_zero(tmp_ctx, struct sdap_domain); + assert_non_null(sdom); + sdom2 = talloc_zero(tmp_ctx, struct sdap_domain); + assert_non_null(sdom2); + + sdom->search_bases = search_bases; + sdom->next = sdom2; + sdom->prev = NULL; + sdom2->search_bases = search_bases2; + sdom2->next = NULL; + sdom2->prev = sdom; + + opts = talloc(tmp_ctx, struct sdap_options); + assert_non_null(opts); + opts->sdom = sdom; + res_sdom = sdap_domain_get_by_dn(opts, dn); + + switch (expected_result) { + case DN_NOT_IN_DOMS: + assert_null(res_sdom); + break; + case DN_IN_DOM1: + assert_true(res_sdom == sdom); + break; + case DN_IN_DOM2: + assert_true(res_sdom == sdom2); + break; + } + + talloc_free(tmp_ctx); +} + +void test_get_by_dn(void **state) +{ + const char *dn = "cn=user, dc=sub, dc=ad, dc=pb"; + const char *dns[] = {"dc=ad, dc=pb"}; + const char *dns2[] = {"dc=sub, dc=ad, dc=pb"}; + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + + do_test_get_by_dn(test_ctx, dn, dns, 1, dns2, 1, DN_IN_DOM2); +} + +void test_get_by_dn2(void **state) +{ + const char *dn = "cn=user, dc=ad, dc=com"; + const char *dns[] = {"dc=ad, dc=com"}; + const char *dns2[] = {"dc=sub, dc=ad, dc=pb"}; + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + + do_test_get_by_dn(test_ctx, dn, dns, 1, dns2, 1, DN_IN_DOM1); +} + +void test_get_by_dn_fail(void **state) +{ + const char *dn = "cn=user, dc=sub, dc=example, dc=com"; + const char *dns[] = {"dc=ad, dc=pb"}; + const char *dns2[] = {"dc=sub, dc=ad, dc=pb"}; + struct test_ctx *test_ctx = talloc_get_type(*state, struct test_ctx); + + do_test_get_by_dn(test_ctx, dn, dns, 1, dns2, 1, DN_NOT_IN_DOMS); +} + +void test_sdap_domain_get_by_name(void **state) +{ + struct sdap_options *opts; + struct sss_domain_info dom1 = { 0 }; + dom1.name = discard_const("dom1"); + struct sss_domain_info dom2 = { 0 }; + dom2.name = discard_const("dom2"); + struct sss_domain_info dom3 = { 0 }; + dom3.name = discard_const("dom3"); + int ret; + struct sdap_domain *sdom; + + opts = talloc_zero(NULL, struct sdap_options); + assert_non_null(opts); + + ret = sdap_domain_add(opts, &dom1, NULL); + assert_int_equal(ret, EOK); + + ret = sdap_domain_add(opts, &dom2, NULL); + assert_int_equal(ret, EOK); + + ret = sdap_domain_add(opts, &dom3, NULL); + assert_int_equal(ret, EOK); + + sdom = sdap_domain_get_by_name(opts, NULL); + assert_null(sdom); + + sdom = sdap_domain_get_by_name(opts, "abc"); + assert_null(sdom); + + sdom = sdap_domain_get_by_name(opts, "dom1"); + assert_non_null(sdom); + assert_ptr_equal(sdom->dom, &dom1); + + sdom = sdap_domain_get_by_name(opts, "dom2"); + assert_non_null(sdom); + assert_ptr_equal(sdom->dom, &dom2); + + sdom = sdap_domain_get_by_name(opts, "dom3"); + assert_non_null(sdom); + assert_ptr_equal(sdom->dom, &dom3); + + talloc_free(opts); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_search_bases_fail, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_search_bases_success, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_get_by_dn_fail, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_get_by_dn, + test_setup, + test_teardown), + cmocka_unit_test_setup_teardown(test_get_by_dn2, + test_setup, + test_teardown), + + cmocka_unit_test(test_sdap_domain_get_by_name) + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_simple_access.c b/src/tests/cmocka/test_simple_access.c new file mode 100644 index 0000000..24d63b4 --- /dev/null +++ b/src/tests/cmocka/test_simple_access.c @@ -0,0 +1,836 @@ +/* + Copyright (C) 2015 Red Hat + + SSSD tests: Simple access provider tests + + 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 <talloc.h> +#include <tevent.h> +#include <errno.h> +#include <popt.h> +#include <security/pam_appl.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_be.h" +#include "tests/cmocka/common_mock_resp.h" +#include "db/sysdb_private.h" /* new_subdomain() */ +#include "providers/simple/simple_access.h" +#include "providers/simple/simple_access_pvt.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_simple_conf.ldb" +#define TEST_DOM_NAME "simple_test" +#define TEST_SUBDOM_NAME "test.subdomain" +#define TEST_ID_PROVIDER "ldap" + +struct simple_test_ctx { + struct sss_test_ctx *tctx; + struct be_ctx *be_ctx; + struct sss_domain_info *subdom; + + bool access_granted; + struct simple_ctx *ctx; + struct pam_data *pd; + struct dp_req_params *params; +}; + +static int test_simple_setup(struct sss_test_conf_param params[], void **state) +{ + struct simple_test_ctx *simple_test_ctx; + int ret; + + simple_test_ctx = talloc_zero(NULL, struct simple_test_ctx); + if (simple_test_ctx == NULL) { + return ENOMEM; + } + + simple_test_ctx->tctx = create_dom_test_ctx(simple_test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, params); + assert_non_null(simple_test_ctx->tctx); + if (simple_test_ctx->tctx == NULL) { + return ENOMEM; + } + + ret = sss_names_init(simple_test_ctx, simple_test_ctx->tctx->confdb, + TEST_DOM_NAME, &simple_test_ctx->tctx->dom->names); + if (ret != EOK) { + return ENOMEM; + } + + simple_test_ctx->be_ctx = mock_be_ctx(simple_test_ctx, + simple_test_ctx->tctx); + if (simple_test_ctx->be_ctx == NULL) { + return ENOMEM; + } + + simple_test_ctx->pd = talloc_zero(simple_test_ctx, struct pam_data); + if (simple_test_ctx->pd == NULL) { + return ENOMEM; + } + simple_test_ctx->pd->cmd = SSS_PAM_ACCT_MGMT; + + simple_test_ctx->params = talloc_zero(simple_test_ctx, + struct dp_req_params); + if (simple_test_ctx->params == NULL) { + return ENOMEM; + } + simple_test_ctx->params->ev = simple_test_ctx->tctx->ev; + + *state = simple_test_ctx; + return 0; +} + +static int set_simple_lists(struct simple_test_ctx *test_ctx, + struct sss_domain_info *dom, + struct sss_test_conf_param params[]) +{ + errno_t ret; + const char *val[2] = { NULL, NULL }; + char *cdb_path; + + cdb_path = talloc_asprintf(test_ctx, CONFDB_DOMAIN_PATH_TMPL, dom->name); + if (cdb_path == NULL) { + return ENOMEM; + } + + ret = EOK; + + if (params != NULL) { + for (int i = 0; params[i].key != NULL; i++) { + val[0] = params[i].value; + ret = confdb_add_param(test_ctx->tctx->confdb, + true, cdb_path, params[i].key, val); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add parameter %s [%d]: " + "%s\n", params[i].key, ret, sss_strerror(ret)); + break; + } + } + } + + talloc_free(cdb_path); + return ret; +} + +static int setup_with_params(struct simple_test_ctx *test_ctx, + struct sss_domain_info *dom, + struct sss_test_conf_param params[]) +{ + errno_t ret; + + ret = set_simple_lists(test_ctx, dom, params); + if (ret != EOK) { + return ret; + } + + test_ctx->ctx = talloc_zero(test_ctx, struct simple_ctx); + if (test_ctx->ctx == NULL) { + return ENOMEM; + } + + test_ctx->ctx->be_ctx = test_ctx->be_ctx; + test_ctx->ctx->domain = test_ctx->tctx->dom; + + return EOK; +} + +static int simple_test_setup(void **state) +{ + test_dom_suite_setup(TESTS_PATH); + return test_simple_setup(NULL, state); +} + +static int simple_test_teardown(void **state) +{ + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + + /* make sure there are no leftovers from previous tests */ + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + talloc_free(simple_test_ctx); + return 0; +} + +static void simple_access_handler_done(struct tevent_req *req) +{ + struct simple_test_ctx *simple_test_ctx = + tevent_req_callback_data(req, struct simple_test_ctx); + + simple_test_ctx->tctx->error = simple_access_handler_recv(simple_test_ctx, + req, &simple_test_ctx->pd); + simple_test_ctx->access_granted = (simple_test_ctx->pd->pam_status == PAM_SUCCESS); + talloc_free(req); + simple_test_ctx->tctx->done = true; +} + +static void run_simple_access_check(struct simple_test_ctx *simple_test_ctx, + const char *username, + int expected_rv, + bool allow_access) +{ + int ret; + struct tevent_req *req; + + simple_test_ctx->tctx->done = false; + simple_test_ctx->pd->user = discard_const(username); + req = simple_access_handler_send(simple_test_ctx, + simple_test_ctx->ctx, + simple_test_ctx->pd, + simple_test_ctx->params); + assert_non_null(req); + tevent_req_set_callback(req, simple_access_handler_done, simple_test_ctx); + + ret = test_ev_loop(simple_test_ctx->tctx); + assert_int_equal(ret, expected_rv); + + /* otherwise the output is undefined */ + if (expected_rv == EOK) { + assert_true(simple_test_ctx->access_granted == allow_access); + } +} + +static void test_both_empty(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + + ret = setup_with_params(simple_test_ctx, simple_test_ctx->tctx->dom, NULL); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, true); +} + +static void test_allow_empty(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_deny_users", "u1, u2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, simple_test_ctx->tctx->dom, params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false); + run_simple_access_check(simple_test_ctx, "u3@simple_test", EOK, true); +} + +static void test_deny_empty(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_users", "u1, u2" }, + { NULL, NULL }, + }; + ret = setup_with_params(simple_test_ctx, simple_test_ctx->tctx->dom, params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, true); + run_simple_access_check(simple_test_ctx, "u3@simple_test", EOK, false); +} + +static void test_both_set(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_users", "u1, u2" }, + { "simple_deny_users", "u1, u2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false); + run_simple_access_check(simple_test_ctx, "u3@simple_test", EOK, false); +} + +static void test_deny_wrong_case(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_users", "u1, u2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "U1@simple_test", EOK, false); +} + +static void test_allow_case_insensitive(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_users", "u1, u2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + simple_test_ctx->tctx->dom->case_sensitive = false; + run_simple_access_check(simple_test_ctx, "U1@simple_test", EOK, true); +} + +static void test_unknown_user(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_users", "u1, u2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "foo@simple_test", EOK, false); +} + +static void test_space(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_users", "space user, another user@simple_test" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, simple_test_ctx->tctx->dom, params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "space user@simple_test", EOK, true); + run_simple_access_check(simple_test_ctx, "another user@simple_test", EOK, true); + run_simple_access_check(simple_test_ctx, "not allowed@simple_test", EOK, false); +} + +static int simple_group_test_setup(void **state) +{ + int ret; + char *u1; + char *u2; + char *u3; + char *g1; + char *g2; + char *sp; + char *sp2; + char *pvt; + struct simple_test_ctx *test_ctx; + + ret = simple_test_setup((void **) &test_ctx); + if (ret != EOK) { + return 1; + } + + u1 = sss_create_internal_fqname(test_ctx, "u1", + test_ctx->be_ctx->domain->name); + u2 = sss_create_internal_fqname(test_ctx, "u2", + test_ctx->be_ctx->domain->name); + u3 = sss_create_internal_fqname(test_ctx, "u3", + test_ctx->be_ctx->domain->name); + g1 = sss_create_internal_fqname(test_ctx, "g1", + test_ctx->be_ctx->domain->name); + g2 = sss_create_internal_fqname(test_ctx, "g2", + test_ctx->be_ctx->domain->name); + sp = sss_create_internal_fqname(test_ctx, "space group", + test_ctx->be_ctx->domain->name); + sp2 = sss_create_internal_fqname(test_ctx, "another space", + test_ctx->be_ctx->domain->name); + pvt = sss_create_internal_fqname(test_ctx, "pvt", + test_ctx->be_ctx->domain->name); + if (u1 == NULL || u2 == NULL || u3 == NULL + || g1 == NULL || g2 == NULL || pvt == NULL + || sp == NULL || sp2 == NULL) { + return 1; + } + + ret = sysdb_add_group(test_ctx->be_ctx->domain, pvt, 999, NULL, 0, 0); + if (ret != EOK) return 1; + + ret = sysdb_store_user(test_ctx->be_ctx->domain, + u1, NULL, 123, 999, "u1", "/home/u1", + "/bin/bash", NULL, NULL, NULL, -1, 0); + if (ret != EOK) return 1; + + ret = sysdb_store_user(test_ctx->be_ctx->domain, + u2, NULL, 456, 999, "u1", "/home/u1", + "/bin/bash", NULL, NULL, NULL, -1, 0); + if (ret != EOK) return 1; + + ret = sysdb_store_user(test_ctx->be_ctx->domain, + u3, NULL, 789, 999, "u1", "/home/u1", + "/bin/bash", NULL, NULL, NULL, -1, 0); + if (ret != EOK) return 1; + + ret = sysdb_add_group(test_ctx->be_ctx->domain, g1, 321, NULL, 0, 0); + if (ret != EOK) return 1; + + ret = sysdb_add_group(test_ctx->be_ctx->domain, g2, 654, NULL, 0, 0); + if (ret != EOK) return 1; + + ret = sysdb_add_group(test_ctx->be_ctx->domain, sp, 1234, NULL, 0, 0); + if (ret != EOK) return 1; + + ret = sysdb_add_group(test_ctx->be_ctx->domain, sp2, 5678, NULL, 0, 0); + if (ret != EOK) return 1; + + ret = sysdb_add_group_member(test_ctx->be_ctx->domain, + g1, u1, SYSDB_MEMBER_USER, false); + if (ret != EOK) return 1; + + ret = sysdb_add_group_member(test_ctx->be_ctx->domain, + sp, u1, SYSDB_MEMBER_USER, false); + if (ret != EOK) return 1; + + ret = sysdb_add_group_member(test_ctx->be_ctx->domain, + g2, u2, SYSDB_MEMBER_USER, false); + if (ret != EOK) return 1; + + ret = sysdb_add_group_member(test_ctx->be_ctx->domain, + sp2, u2, SYSDB_MEMBER_USER, false); + if (ret != EOK) return 1; + + *state = test_ctx; + return 0; +} + +static int simple_group_test_teardown(void **state) +{ + int ret; + char *u1; + char *u2; + char *u3; + char *g1; + char *g2; + char *sp; + char *sp2; + char *pvt; + struct simple_test_ctx *test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + + u1 = sss_create_internal_fqname(test_ctx, "u1", + test_ctx->be_ctx->domain->name); + u2 = sss_create_internal_fqname(test_ctx, "u2", + test_ctx->be_ctx->domain->name); + u3 = sss_create_internal_fqname(test_ctx, "u3", + test_ctx->be_ctx->domain->name); + g1 = sss_create_internal_fqname(test_ctx, "g1", + test_ctx->be_ctx->domain->name); + g2 = sss_create_internal_fqname(test_ctx, "g2", + test_ctx->be_ctx->domain->name); + sp = sss_create_internal_fqname(test_ctx, "space group", + test_ctx->be_ctx->domain->name); + sp2 = sss_create_internal_fqname(test_ctx, "another space", + test_ctx->be_ctx->domain->name); + pvt = sss_create_internal_fqname(test_ctx, "pvt", + test_ctx->be_ctx->domain->name); + if (u1 == NULL || u2 == NULL || u3 == NULL + || g1 == NULL || g2 == NULL || pvt == NULL + || sp == NULL || sp2 == NULL) { + return 1; + } + + ret = sysdb_delete_user(test_ctx->be_ctx->domain, u1, 0); + if (ret != EOK) return 1; + ret = sysdb_delete_user(test_ctx->be_ctx->domain, u2, 0); + if (ret != EOK) return 1; + ret = sysdb_delete_user(test_ctx->be_ctx->domain, u3, 0); + if (ret != EOK) return 1; + ret = sysdb_delete_group(test_ctx->be_ctx->domain, g1, 0); + if (ret != EOK) return 1; + ret = sysdb_delete_group(test_ctx->be_ctx->domain, g2, 0); + if (ret != EOK) return 1; + ret = sysdb_delete_group(test_ctx->be_ctx->domain, sp, 0); + if (ret != EOK) return 1; + ret = sysdb_delete_group(test_ctx->be_ctx->domain, sp2, 0); + if (ret != EOK) return 1; + ret = sysdb_delete_group(test_ctx->be_ctx->domain, pvt, 0); + if (ret != EOK) return 1; + + /* make sure there are no leftovers from previous tests */ + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + talloc_free(test_ctx); + return 0; +} + +static void test_group_allow_empty(void **state) +{ + errno_t ret; + struct tevent_req *req; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_deny_groups", "g1, g2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + simple_test_ctx->pd->user = discard_const("u1@simple_test"); + req = simple_access_handler_send(simple_test_ctx, simple_test_ctx->ctx, + simple_test_ctx->pd, + simple_test_ctx->params); + assert_non_null(req); + tevent_req_set_callback(req, simple_access_handler_done, simple_test_ctx); + + ret = test_ev_loop(simple_test_ctx->tctx); + assert_int_equal(ret, EOK); + assert_false(simple_test_ctx->access_granted); + + simple_test_ctx->tctx->done = false; + simple_test_ctx->pd->user = discard_const("u3@simple_test"); + req = simple_access_handler_send(simple_test_ctx, simple_test_ctx->ctx, + simple_test_ctx->pd, + simple_test_ctx->params); + assert_non_null(req); + tevent_req_set_callback(req, simple_access_handler_done, simple_test_ctx); + + ret = test_ev_loop(simple_test_ctx->tctx); + assert_int_equal(ret, EOK); + assert_true(simple_test_ctx->access_granted); +} + +static void test_group_deny_empty(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_groups", "g1, g2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, simple_test_ctx->tctx->dom, params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, true); + run_simple_access_check(simple_test_ctx, "u3@simple_test", EOK, false); +} + +static void test_group_both_set(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_groups", "g1, g2" }, + { "simple_deny_groups", "g1, g2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, simple_test_ctx->tctx->dom, params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false); + run_simple_access_check(simple_test_ctx, "u3@simple_test", EOK, false); +} + +static void test_group_deny_wrong_case(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_groups", "G1, G2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false); +} + +static void test_group_allow_case_insensitive(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_groups", "G1, G2" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + /* Case-sensitive domain, wrong case */ + simple_test_ctx->tctx->done = false; + simple_test_ctx->tctx->dom->case_sensitive = false; + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, true); +} + +static void test_unparseable_allow_user(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_users", "u1, user@no.such.domain" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + /* Case-sensitive domain, wrong case */ + simple_test_ctx->tctx->done = false; + simple_test_ctx->tctx->dom->case_sensitive = false; + /* A user that would normally be denied access will be denied because + * the access list can't be parsed + */ + run_simple_access_check(simple_test_ctx, "u2@simple_test", EOK, false); + /* A user that would normally be allowed access will be denied because + * the access list can't be parsed + */ + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false); +} + +static void test_unparseable_deny_user(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_deny_users", "u2, user@no.such.domain" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + /* Case-sensitive domain, wrong case */ + simple_test_ctx->tctx->done = false; + simple_test_ctx->tctx->dom->case_sensitive = false; + /* A user that would normally be denied access will be denied because + * the access list can't be parsed + */ + run_simple_access_check(simple_test_ctx, "u2@simple_test", EOK, false); + /* A user that would normally be allowed access will be denied because + * the access list can't be parsed + */ + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false); +} + +static void test_unparseable_allow_group(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_groups", "g1, group@no.such.domain" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + /* Case-sensitive domain, wrong case */ + simple_test_ctx->tctx->done = false; + simple_test_ctx->tctx->dom->case_sensitive = false; + /* A group that would normally be denied access will be denied because + * the access list can't be parsed + */ + run_simple_access_check(simple_test_ctx, "u2@simple_test", EOK, false); + /* A group that would normally be allowed access will be denied because + * the access list can't be parsed + */ + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false); +} + +static void test_unparseable_deny_group(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_deny_groups", "g2, group@no.such.domain" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, + simple_test_ctx->tctx->dom, + params); + assert_int_equal(ret, EOK); + + /* Case-sensitive domain, wrong case */ + simple_test_ctx->tctx->done = false; + simple_test_ctx->tctx->dom->case_sensitive = false; + /* A group that would normally be denied access will be denied because + * the access list can't be parsed + */ + run_simple_access_check(simple_test_ctx, "u2@simple_test", EOK, false); + /* A group that would normally be allowed access will be denied because + * the access list can't be parsed + */ + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, false); +} + +static void test_group_space(void **state) +{ + errno_t ret; + struct simple_test_ctx *simple_test_ctx = \ + talloc_get_type(*state, struct simple_test_ctx); + struct sss_test_conf_param params[] = { + { "simple_allow_groups", "space group, another space@simple_test" }, + { NULL, NULL }, + }; + + ret = setup_with_params(simple_test_ctx, simple_test_ctx->tctx->dom, params); + assert_int_equal(ret, EOK); + + run_simple_access_check(simple_test_ctx, "u1@simple_test", EOK, true); + run_simple_access_check(simple_test_ctx, "u2@simple_test", EOK, true); + run_simple_access_check(simple_test_ctx, "u3@simple_test", EOK, false); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_both_empty, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_allow_empty, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_deny_empty, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_both_set, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_deny_wrong_case, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_allow_case_insensitive, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_unknown_user, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_space, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_group_allow_empty, + simple_group_test_setup, + simple_group_test_teardown), + cmocka_unit_test_setup_teardown(test_group_deny_empty, + simple_group_test_setup, + simple_group_test_teardown), + cmocka_unit_test_setup_teardown(test_group_both_set, + simple_group_test_setup, + simple_group_test_teardown), + cmocka_unit_test_setup_teardown(test_group_deny_wrong_case, + simple_group_test_setup, + simple_group_test_teardown), + cmocka_unit_test_setup_teardown(test_group_allow_case_insensitive, + simple_group_test_setup, + simple_group_test_teardown), + cmocka_unit_test_setup_teardown(test_group_space, + simple_group_test_setup, + simple_group_test_teardown), + cmocka_unit_test_setup_teardown(test_unparseable_allow_user, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_unparseable_deny_user, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_unparseable_allow_group, + simple_test_setup, + simple_test_teardown), + cmocka_unit_test_setup_teardown(test_unparseable_deny_group, + simple_test_setup, + simple_test_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + return rv; +} diff --git a/src/tests/cmocka/test_ssh_srv.c b/src/tests/cmocka/test_ssh_srv.c new file mode 100644 index 0000000..dbe3621 --- /dev/null +++ b/src/tests/cmocka/test_ssh_srv.c @@ -0,0 +1,1129 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2015 Red Hat + + SSSD tests: PAM responder tests + + 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 <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/cmocka/common_mock_resp.h" +#include "responder/common/responder_packet.h" +#include "responder/common/negcache.h" +#include "responder/ssh/ssh_private.h" +#include "confdb/confdb.h" + +#include "util/crypto/sss_crypto.h" + +#ifdef HAVE_TEST_CA +#include "tests/test_CA/SSSD_test_cert_x509_0001.h" +#include "tests/test_CA/SSSD_test_cert_pubsshkey_0001.h" +#include "tests/test_CA/SSSD_test_cert_x509_0002.h" +#include "tests/test_CA/SSSD_test_cert_pubsshkey_0002.h" +#include "tests/test_CA/SSSD_test_cert_x509_0007.h" +#include "tests/test_CA/SSSD_test_cert_pubsshkey_0007.h" +#else +#define SSSD_TEST_CERT_0001 "" +#define SSSD_TEST_CERT_SSH_KEY_0001 "" +#define SSSD_TEST_CERT_0002 "" +#define SSSD_TEST_CERT_SSH_KEY_0002 "" +#define SSSD_TEST_CERT_0007 "" +#define SSSD_TEST_CERT_SSH_KEY_0007 "" +#endif + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_ssh_conf.ldb" +#define TEST_DOM_NAME "ssh_test" +#define TEST_SUBDOM_NAME "test.subdomain" +#define TEST_ID_PROVIDER "ldap" + +#define TEST_SSH_PUBKEY \ +"AAAAB3NzaC1yc2EAAAADAQABAAABAQC1" \ +"OlYGkYw8JyhKQrlNBGbZC2az9TJhUWNn" \ +"/kS26OOI9hXCZgz4eHyZnCS1bY1/0ptG" \ +"ByQAk2qvF9uYV2plxULoiOUYAWCnnqx/" \ +"bnhQ4SxmCcA5RPy3h8FX2OrxMlQEadH6" \ +"wz3ZTnOvsw57/ZV8yXjzVexJeeO1A59g" \ +"pLD43f3v056zSF/Jo1NwAZUzCJuzpFAy" \ +"Ale6mZ/1rpGN+ah6rN70wz3brwEOi4f2" \ +"HQNbKAL4idVyRYbA7oU+htCLEd6YsSdy" \ +"murxDMAEEQbLeMbF1DXNt1OunoeprXrU" \ +"UE1U9Rxi6xvPt7s3h9NbZiaLRPJU6due" \ +"+nqwn8En7mesd7LnRQST" + +struct ssh_test_ctx { + struct sss_test_ctx *tctx; + struct sss_domain_info *subdom; + + struct resp_ctx *rctx; + struct cli_ctx *cctx; + struct sss_cmd_table *ssh_cmds; + struct ssh_ctx *ssh_ctx; + + int ncache_hits; + bool provider_contacted; + + const char *ssh_user_fqdn; + const char *wrong_user_fqdn; +}; + +/* Must be global because it is needed in some wrappers */ +struct ssh_test_ctx *ssh_test_ctx; + +struct ssh_ctx *mock_ssh_ctx(TALLOC_CTX *mem_ctx) +{ + struct ssh_ctx *ssh_ctx; + + ssh_ctx = talloc_zero(mem_ctx, struct ssh_ctx); + assert_non_null(ssh_ctx); + + return ssh_ctx; +} + +static int add_confdb_params(struct sss_test_conf_param params[], + struct confdb_ctx *cdb, const char *section) +{ + const char *val[2]; + int ret; + + val[1] = NULL; + + for (int i = 0; params[i].key; i++) { + val[0] = params[i].value; + ret = confdb_add_param(cdb, true, section, params[i].key, val); + assert_int_equal(ret, EOK); + } + + return EOK; +} + +static int add_ssh_params(struct sss_test_conf_param ssh_params[], + struct confdb_ctx *cdb) +{ + return add_confdb_params(ssh_params, cdb, CONFDB_SSH_CONF_ENTRY); +} + +static int add_monitor_params(struct sss_test_conf_param monitor_params[], + struct confdb_ctx *cdb) +{ + return add_confdb_params(monitor_params, cdb, CONFDB_MONITOR_CONF_ENTRY); +} + +void test_ssh_setup(struct sss_test_conf_param dom_params[], + struct sss_test_conf_param ssh_params[], + struct sss_test_conf_param monitor_params[], + void **state) +{ + struct cli_protocol *prctx; + errno_t ret; + + ssh_test_ctx = talloc_zero(NULL, struct ssh_test_ctx); + assert_non_null(ssh_test_ctx); + + ssh_test_ctx->tctx = create_dom_test_ctx(ssh_test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, dom_params); + assert_non_null(ssh_test_ctx->tctx); + + ssh_test_ctx->ssh_cmds = get_ssh_cmds(); + assert_non_null(ssh_test_ctx->ssh_cmds); + + /* FIXME - perhaps this should be folded into sssd_domain_init or strictly + * used together + */ + ret = sss_names_init(ssh_test_ctx, ssh_test_ctx->tctx->confdb, + TEST_DOM_NAME, &ssh_test_ctx->tctx->dom->names); + assert_int_equal(ret, EOK); + + /* Initialize the SSH responder */ + ssh_test_ctx->ssh_ctx = mock_ssh_ctx(ssh_test_ctx); + assert_non_null(ssh_test_ctx->ssh_ctx); + + ssh_test_ctx->rctx = mock_rctx(ssh_test_ctx, ssh_test_ctx->tctx->ev, + ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_ctx); + assert_non_null(ssh_test_ctx->rctx); + ssh_test_ctx->rctx->cdb = ssh_test_ctx->tctx->confdb; + ssh_test_ctx->ssh_ctx->rctx = ssh_test_ctx->rctx; + + ret = add_ssh_params(ssh_params, ssh_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + ret = add_monitor_params(monitor_params, ssh_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + + /* Create client context */ + ssh_test_ctx->cctx = mock_cctx(ssh_test_ctx, ssh_test_ctx->rctx); + assert_non_null(ssh_test_ctx->cctx); + ssh_test_ctx->cctx->ev = ssh_test_ctx->tctx->ev; + + prctx = mock_prctx(ssh_test_ctx->cctx); + assert_non_null(prctx); + ssh_test_ctx->cctx->protocol_ctx = prctx; + prctx->cli_protocol_version = register_cli_protocol_version(); +} + +static void ssh_test_setup_common(void) +{ + errno_t ret; + + ssh_test_ctx->ssh_user_fqdn = \ + sss_create_internal_fqname(ssh_test_ctx, + "sshuser", + ssh_test_ctx->tctx->dom->name); + assert_non_null(ssh_test_ctx->ssh_user_fqdn); + + ssh_test_ctx->wrong_user_fqdn = \ + sss_create_internal_fqname(ssh_test_ctx, + "wrongsshuser", + ssh_test_ctx->tctx->dom->name); + assert_non_null(ssh_test_ctx->wrong_user_fqdn); + + /* Prime the cache with a valid user */ + ret = sysdb_add_user(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + 123, 456, "ssh user", + "/home/sshuser", "/bin/sh", NULL, + NULL, 300, 0); + assert_int_equal(ret, EOK); + + /* Prime the cache with a user for wrong matches */ + ret = sysdb_add_user(ssh_test_ctx->tctx->dom, + ssh_test_ctx->wrong_user_fqdn, + 321, 654, "wrong ssh user", + "/home/wrongsshuser", "/bin/sh", NULL, + NULL, 300, 0); + assert_int_equal(ret, EOK); +} + +static int ssh_test_setup(void **state) +{ + struct sss_test_conf_param dom_params[] = { + { "enumerate", "false" }, + { "cache_credentials", "true" }, + { NULL, NULL }, /* Sentinel */ + }; + + /* When run under valgrind with --trace-children=yes we have to increase + * the timeout not because p11_child needs much more time under valgrind + * but because of the way valgrind handles the children. */ + struct sss_test_conf_param ssh_params[] = { + { "p11_child_timeout", "80" }, + { NULL, NULL }, /* Sentinel */ + }; + + struct sss_test_conf_param monitor_params[] = { + { "certificate_verification", "no_ocsp"}, + { NULL, NULL }, /* Sentinel */ + }; + + test_ssh_setup(dom_params, ssh_params, monitor_params, state); + + ssh_test_setup_common(); + return 0; +} + +static int ssh_test_teardown(void **state) +{ + int ret; + + ret = sysdb_delete_user(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, 0); + assert_int_equal(ret, EOK); + + ret = sysdb_delete_user(ssh_test_ctx->tctx->dom, + ssh_test_ctx->wrong_user_fqdn, 0); + assert_int_equal(ret, EOK); + + talloc_free(ssh_test_ctx); + return 0; +} + +typedef int (*cmd_cb_fn_t)(uint32_t, uint8_t *, size_t); + + +int __real_read_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **buf, ssize_t *len); + +void __real_sss_packet_get_body(struct sss_packet *packet, + uint8_t **body, size_t *blen); + +void __wrap_sss_packet_get_body(struct sss_packet *packet, + uint8_t **body, size_t *blen) +{ + enum sss_test_wrapper_call wtype = sss_mock_type(enum sss_test_wrapper_call); + size_t len; + + if (wtype == WRAP_CALL_REAL) { + return __real_sss_packet_get_body(packet, body, blen); + } + + *body = sss_mock_ptr_type(uint8_t *); + len = sss_mock_type(size_t); + if (len == 0) { + len = strlen((const char *) *body) + 1; + } + *blen = len; + return; +} + +void __real_sss_packet_get_body(struct sss_packet *packet, + uint8_t **body, size_t *blen); + +void __wrap_sss_cmd_done(struct cli_ctx *cctx, void *freectx) +{ + struct cli_protocol *prctx; + struct sss_packet *packet; + uint8_t *body; + size_t blen; + cmd_cb_fn_t check_cb; + + prctx = talloc_get_type(cctx->protocol_ctx, struct cli_protocol); + packet = prctx->creq->out; + assert_non_null(packet); + + check_cb = sss_mock_ptr_type(cmd_cb_fn_t); + + __real_sss_packet_get_body(packet, &body, &blen); + + ssh_test_ctx->tctx->error = check_cb(sss_packet_get_status(packet), + body, blen); + ssh_test_ctx->tctx->done = true; +} + +enum sss_cli_command __wrap_sss_packet_get_cmd(struct sss_packet *packet) +{ + return sss_mock_type(enum sss_cli_command); +} + +int __wrap_sss_cmd_send_empty(struct cli_ctx *cctx, TALLOC_CTX *freectx) +{ + ssh_test_ctx->tctx->done = true; + ssh_test_ctx->tctx->error = ENOENT; + return EOK; +} + +static void set_cmd_cb(cmd_cb_fn_t fn) +{ + will_return(__wrap_sss_cmd_done, fn); +} + +static void mock_input_user(TALLOC_CTX *mem_ctx, const char *username) +{ + uint8_t *buf; + size_t len = strlen(username); + size_t buf_len = len + 1 + 2 * sizeof(uint32_t); + + buf = talloc_size(mem_ctx, buf_len); + SAFEALIGN_SET_UINT32(&buf[0], 0, NULL); + SAFEALIGN_SET_UINT32(&buf[4], len + 1, NULL); + memcpy(&buf[8], username, len + 1); + + will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); + will_return(__wrap_sss_packet_get_body, buf); + will_return(__wrap_sss_packet_get_body, buf_len); + mock_parse_inp("sshuser", TEST_DOM_NAME, EOK); +} + +static int test_ssh_user_no_pubkeys_check(uint32_t status, + uint8_t *body, size_t blen) +{ + uint32_t val; + + assert_int_equal(status, EOK); + assert_int_equal(blen, 8); + + SAFEALIGN_COPY_UINT32(&val, &body[0], NULL); + assert_int_equal(val, 0); + + SAFEALIGN_COPY_UINT32(&val, &body[4], NULL); + assert_int_equal(val, 0); + + return EOK; +} + +void test_ssh_user_no_pubkeys(void **state) { + int ret; + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_ssh_user_no_pubkeys_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int test_ssh_user_one_pubkey_check(uint32_t status, + uint8_t *body, size_t blen) +{ + uint32_t val; + size_t exp_len; + size_t name_len; + size_t key_len; + uint8_t *key; + size_t rp = 0; + + key = sss_base64_decode(ssh_test_ctx, TEST_SSH_PUBKEY, &key_len); + name_len = strlen(ssh_test_ctx->ssh_user_fqdn) + 1; + + exp_len = 5 * sizeof(uint32_t) + name_len + key_len; + + assert_int_equal(status, EOK); + assert_int_equal(blen, exp_len); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 1); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 0); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 0); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, name_len); + + assert_memory_equal(body + rp, ssh_test_ctx->ssh_user_fqdn, name_len); + rp += name_len; + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, key_len); + + assert_memory_equal(body + rp, key, key_len); + rp += key_len; + + assert_int_equal(rp, blen); + + return EOK; +} + +void test_ssh_user_pubkey(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_ssh_user_one_pubkey_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_ssh_user_pubkey_cert_disabled(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_string(attrs, SYSDB_USER_CERT, SSSD_TEST_CERT_0001); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_string(attrs, SYSDB_USER_CERT, SSSD_TEST_CERT_0002); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_ssh_user_one_pubkey_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +static int test_ssh_user_pubkey_cert_pss_check(uint32_t status, + uint8_t *body, size_t blen) +{ + uint32_t val; + size_t exp_len; + size_t name_len; + size_t key_len[2]; + uint8_t *key[2]; + size_t rp = 0; + size_t c; + + key[0] = sss_base64_decode(ssh_test_ctx, TEST_SSH_PUBKEY, &key_len[0]); + assert_non_null(key[0]); + + key[1] = sss_base64_decode(ssh_test_ctx, SSSD_TEST_CERT_SSH_KEY_0007, + &key_len[1]); + assert_non_null(key[1]); + + name_len = strlen(ssh_test_ctx->ssh_user_fqdn) + 1; + + exp_len = 2 * sizeof(uint32_t) + 2* 3* sizeof(uint32_t) + 2 * name_len + + key_len[0] + key_len[1]; + + assert_int_equal(status, EOK); + assert_int_equal(blen, exp_len); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 2); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 0); + + for (c = 0; c < 2; c++) { + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 0); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, name_len); + + assert_memory_equal(body + rp, ssh_test_ctx->ssh_user_fqdn, name_len); + rp += name_len; + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, key_len[c]); + + assert_memory_equal(body + rp, key[c], key_len[c]); + rp += key_len[c]; + } + + assert_int_equal(rp, blen); + + return EOK; +} + +static int test_ssh_user_pubkey_cert_check(uint32_t status, + uint8_t *body, size_t blen) +{ + uint32_t val; + size_t exp_len; + size_t name_len; + size_t key_len[3]; + uint8_t *key[3]; + size_t rp = 0; + size_t c; + + key[0] = sss_base64_decode(ssh_test_ctx, TEST_SSH_PUBKEY, &key_len[0]); + assert_non_null(key[0]); + + key[1] = sss_base64_decode(ssh_test_ctx, SSSD_TEST_CERT_SSH_KEY_0001, + &key_len[1]); + assert_non_null(key[1]); + + key[2] = sss_base64_decode(ssh_test_ctx, SSSD_TEST_CERT_SSH_KEY_0002, + &key_len[2]); + assert_non_null(key[2]); + + name_len = strlen(ssh_test_ctx->ssh_user_fqdn) + 1; + + exp_len = 2 * sizeof(uint32_t) + 3* 3* sizeof(uint32_t) + 3 * name_len + + key_len[0] + key_len[1] + key_len[2]; + + assert_int_equal(status, EOK); + assert_int_equal(blen, exp_len); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 3); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 0); + + for (c = 0; c < 3; c++) { + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 0); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, name_len); + + assert_memory_equal(body + rp, ssh_test_ctx->ssh_user_fqdn, name_len); + rp += name_len; + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, key_len[c]); + + assert_memory_equal(body + rp, key[c], key_len[c]); + rp += key_len[c]; + } + + assert_int_equal(rp, blen); + + return EOK; +} + +static int test_ssh_user_pubkey_cert_1_check(uint32_t status, + uint8_t *body, size_t blen) +{ + uint32_t val; + size_t exp_len; + size_t name_len; + size_t key_len[2]; + uint8_t *key[2]; + size_t rp = 0; + size_t c; + + key[0] = sss_base64_decode(ssh_test_ctx, TEST_SSH_PUBKEY, &key_len[0]); + assert_non_null(key[0]); + + key[1] = sss_base64_decode(ssh_test_ctx, SSSD_TEST_CERT_SSH_KEY_0001, + &key_len[1]); + assert_non_null(key[1]); + + name_len = strlen(ssh_test_ctx->ssh_user_fqdn) + 1; + + exp_len = 2 * sizeof(uint32_t) + 2* 3* sizeof(uint32_t) + 2 * name_len + + key_len[0] + key_len[1]; + + assert_int_equal(status, EOK); + assert_int_equal(blen, exp_len); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 2); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 0); + + for (c = 0; c < 2; c++) { + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, 0); + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, name_len); + + assert_memory_equal(body + rp, ssh_test_ctx->ssh_user_fqdn, name_len); + rp += name_len; + + SAFEALIGN_COPY_UINT32(&val, &body[rp], &rp); + assert_int_equal(val, key_len[c]); + + assert_memory_equal(body + rp, key[c], key_len[c]); + rp += key_len[c]; + } + + assert_int_equal(rp, blen); + + return EOK; +} + +void test_ssh_user_pubkey_cert(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0001); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0002); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Enable certificate support */ + ssh_test_ctx->ssh_ctx->use_cert_keys = true; + ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR + "/src/tests/test_CA/SSSD_test_CA.pem"); + + set_cmd_cb(test_ssh_user_pubkey_cert_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_ssh_user_pubkey_pss_cert(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0007); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Enable certificate support */ + ssh_test_ctx->ssh_ctx->use_cert_keys = true; + ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR + "/src/tests/test_CA/SSSD_test_CA.pem"); + + set_cmd_cb(test_ssh_user_pubkey_cert_pss_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +struct certmap_info rule_1 = { + discard_const("rule1"), -1, + discard_const("<SUBJECT>CN=SSSD test cert 0001,.*"), + NULL, NULL }; +struct certmap_info rule_2 = { + discard_const("rule2"), -1, + discard_const("<SUBJECT>CN=SSSD test cert 0002,.*"), + NULL, NULL }; + +void test_ssh_user_pubkey_cert_with_rule(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + /* Both rules are enabled, both certificates should be handled. */ + struct certmap_info *certmap_list[] = { &rule_1, &rule_2, NULL}; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0001); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0002); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Enable certificate support */ + ssh_test_ctx->ssh_ctx->use_cert_keys = true; + ssh_test_ctx->ssh_ctx->rctx->domains->certmaps = certmap_list; + ssh_test_ctx->ssh_ctx->certmap_last_read = 0; + ssh_test_ctx->ssh_ctx->rctx->get_domains_last_call.tv_sec = 1; + ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR + "/src/tests/test_CA/SSSD_test_CA.pem"); + + set_cmd_cb(test_ssh_user_pubkey_cert_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_ssh_user_pubkey_cert_with_all_rules(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + /* Both rules are enabled, both certificates should be handled. */ + const char *rule_list[] = { "all_rules", NULL }; + struct certmap_info *certmap_list[] = { &rule_1, &rule_2, NULL}; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0001); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0002); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Enable certificate support */ + ssh_test_ctx->ssh_ctx->use_cert_keys = true; + ssh_test_ctx->ssh_ctx->rctx->domains->certmaps = certmap_list; + ssh_test_ctx->ssh_ctx->certmap_last_read = 0; + ssh_test_ctx->ssh_ctx->rctx->get_domains_last_call.tv_sec = 1; + ssh_test_ctx->ssh_ctx->cert_rules = discard_const(rule_list); + ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR + "/src/tests/test_CA/SSSD_test_CA.pem"); + + set_cmd_cb(test_ssh_user_pubkey_cert_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_ssh_user_pubkey_cert_with_all_rules_but_no_rules_present(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + /* Both rules are enabled, both certificates should be handled. */ + const char *rule_list[] = { "all_rules", NULL }; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0001); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0002); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Enable certificate support */ + ssh_test_ctx->ssh_ctx->use_cert_keys = true; + ssh_test_ctx->ssh_ctx->rctx->domains->certmaps = NULL; + ssh_test_ctx->ssh_ctx->certmap_last_read = 0; + ssh_test_ctx->ssh_ctx->rctx->get_domains_last_call.tv_sec = 1; + ssh_test_ctx->ssh_ctx->cert_rules = discard_const(rule_list); + ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR + "/src/tests/test_CA/SSSD_test_CA.pem"); + + set_cmd_cb(test_ssh_user_pubkey_cert_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_ssh_user_pubkey_cert_with_no_rules(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + /* No rules should be used, both certificates should be handled. */ + const char *rule_list[] = { "no_rules", NULL }; + struct certmap_info *certmap_list[] = { &rule_1, &rule_2, NULL}; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0001); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0002); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Enable certificate support */ + ssh_test_ctx->ssh_ctx->use_cert_keys = true; + ssh_test_ctx->ssh_ctx->rctx->domains->certmaps = certmap_list; + ssh_test_ctx->ssh_ctx->certmap_last_read = 0; + ssh_test_ctx->ssh_ctx->rctx->get_domains_last_call.tv_sec = 1; + ssh_test_ctx->ssh_ctx->cert_rules = discard_const(rule_list); + ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR + "/src/tests/test_CA/SSSD_test_CA.pem"); + + set_cmd_cb(test_ssh_user_pubkey_cert_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_ssh_user_pubkey_cert_with_unknow_rule_name(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + /* No rule is enabled because the unknown rule name "none" is used, both + * certificates should be handled. */ + const char *rule_list[] = { "none", NULL }; + struct certmap_info *certmap_list[] = { &rule_1, &rule_2, NULL}; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0001); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0002); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Enable certificate support */ + ssh_test_ctx->ssh_ctx->use_cert_keys = true; + ssh_test_ctx->ssh_ctx->rctx->domains->certmaps = certmap_list; + ssh_test_ctx->ssh_ctx->certmap_last_read = 0; + ssh_test_ctx->ssh_ctx->rctx->get_domains_last_call.tv_sec = 1; + ssh_test_ctx->ssh_ctx->cert_rules = discard_const(rule_list); + ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR + "/src/tests/test_CA/SSSD_test_CA.pem"); + + set_cmd_cb(test_ssh_user_one_pubkey_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_ssh_user_pubkey_cert_with_rule_1(void **state) +{ + int ret; + struct sysdb_attrs *attrs; + /* Only "rule1" is selected, only certificate 1 should be handled. */ + const char *rule_list[] = { "rule1", NULL }; + struct certmap_info *certmap_list[] = { &rule_1, &rule_2, NULL}; + + attrs = sysdb_new_attrs(ssh_test_ctx); + assert_non_null(attrs); + ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0001); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, + SSSD_TEST_CERT_0002); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom, + ssh_test_ctx->ssh_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + talloc_free(attrs); + assert_int_equal(ret, EOK); + + mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn); + will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + /* Enable certificate support */ + ssh_test_ctx->ssh_ctx->use_cert_keys = true; + ssh_test_ctx->ssh_ctx->rctx->domains->certmaps = certmap_list; + ssh_test_ctx->ssh_ctx->certmap_last_read = 0; + ssh_test_ctx->ssh_ctx->rctx->get_domains_last_call.tv_sec = 1; + ssh_test_ctx->ssh_ctx->cert_rules = discard_const(rule_list); + ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR + "/src/tests/test_CA/SSSD_test_CA.pem"); + + set_cmd_cb(test_ssh_user_pubkey_cert_1_check); + ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS, + ssh_test_ctx->ssh_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(ssh_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + { "no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_ssh_user_no_pubkeys, + ssh_test_setup, ssh_test_teardown), + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey, + ssh_test_setup, ssh_test_teardown), +#ifdef HAVE_TEST_CA + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_disabled, + ssh_test_setup, ssh_test_teardown), + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert, + ssh_test_setup, ssh_test_teardown), + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_rule, + ssh_test_setup, ssh_test_teardown), + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_all_rules, + ssh_test_setup, ssh_test_teardown), + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_all_rules_but_no_rules_present, + ssh_test_setup, ssh_test_teardown), + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_no_rules, + ssh_test_setup, ssh_test_teardown), + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_unknow_rule_name, + ssh_test_setup, ssh_test_teardown), + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_rule_1, + ssh_test_setup, ssh_test_teardown), + cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_pss_cert, + ssh_test_setup, ssh_test_teardown), +#endif + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0 && !no_cleanup) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + + return rv; +} diff --git a/src/tests/cmocka/test_sss_idmap.c b/src/tests/cmocka/test_sss_idmap.c new file mode 100644 index 0000000..e557019 --- /dev/null +++ b/src/tests/cmocka/test_sss_idmap.c @@ -0,0 +1,781 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: Unit tests for libsss_idmap + + 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 <popt.h> + +#include "tests/cmocka/common_mock.h" + +#include "lib/idmap/sss_idmap.h" + +#define TEST_RANGE_MIN 200000 +#define TEST_RANGE_MAX 399999 +#define TEST_DOM_NAME "test.dom" +#define TEST_DOM_SID "S-1-5-21-123-456-789" +#define TEST_FIRST_RID 0 +#define TEST_EXT_MAPPING true + +#define TEST_2_RANGE_MIN 600000 +#define TEST_2_RANGE_MAX 799999 +#define TEST_2_DOM_NAME "test2.dom" +#define TEST_2_DOM_SID "S-1-5-21-987-654-321" +#define TEST_2_FIRST_RID 1000000 +#define TEST_2_EXT_MAPPING true + +#define TEST_OFFSET 1000000 +#define TEST_OFFSET_STR "1000000" + +const int TEST_2922_MIN_ID = 1842600000; +const int TEST_2922_MAX_ID = 1842799999; + +struct test_ctx { + TALLOC_CTX *mem_idmap; + struct sss_idmap_ctx *idmap_ctx; +}; + +static void *idmap_talloc(size_t size, void *pvt) +{ + return talloc_size(pvt, size); +} + +static void idmap_free(void *ptr, void *pvt) +{ + talloc_free(ptr); +} + +static int test_sss_idmap_setup(void **state) +{ + struct test_ctx *test_ctx; + enum idmap_error_code err; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct test_ctx); + assert_non_null(test_ctx); + + check_leaks_push(test_ctx); + + test_ctx->mem_idmap = talloc_new(test_ctx); + assert_non_null(test_ctx->mem_idmap); + + err = sss_idmap_init(idmap_talloc, test_ctx->mem_idmap, idmap_free, + &test_ctx->idmap_ctx); + assert_int_equal(err, IDMAP_SUCCESS); + + *state = test_ctx; + return 0; +} + +static int setup_ranges(struct test_ctx *test_ctx, bool external_mapping, + bool second_domain, bool sec_slices) +{ + struct sss_idmap_range range; + enum idmap_error_code err; + const char *name; + const char *sid; + + assert_non_null(test_ctx); + + if (second_domain) { + range.min = TEST_2_RANGE_MIN; + range.max = TEST_2_RANGE_MAX; + name = TEST_2_DOM_NAME; + sid = TEST_2_DOM_SID; + } else { + range.min = TEST_RANGE_MIN; + range.max = TEST_RANGE_MAX; + name = TEST_DOM_NAME; + sid = TEST_DOM_SID; + } + + if (sec_slices) { + err = sss_idmap_add_auto_domain_ex(test_ctx->idmap_ctx, name, sid, + &range, NULL, 0, external_mapping, + NULL, NULL); + } else { + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, name, sid, &range, + NULL, 0, external_mapping); + } + assert_int_equal(err, IDMAP_SUCCESS); + + range.min += TEST_OFFSET; + range.max += TEST_OFFSET; + + if (sec_slices) { + err = sss_idmap_add_auto_domain_ex(test_ctx->idmap_ctx, name, sid, + &range, NULL, TEST_OFFSET, + external_mapping, NULL, NULL); + } else { + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, name, sid, &range, + NULL, TEST_OFFSET, external_mapping); + } + assert_int_equal(err, IDMAP_SUCCESS); + return 0; +} + +static int setup_ranges_2922(struct test_ctx *test_ctx) +{ + const int TEST_2922_DFL_SLIDE = 9212; + struct sss_idmap_range range; + enum idmap_error_code err; + const char *name; + const char *sid; + /* Pick a new slice. */ + id_t slice_num = -1; + + assert_non_null(test_ctx); + + name = TEST_DOM_NAME; + sid = TEST_DOM_SID; + + err = sss_idmap_calculate_range(test_ctx->idmap_ctx, sid, &slice_num, + &range); + assert_int_equal(err, IDMAP_SUCCESS); + /* Range computation should be deterministic. Lets validate that. */ + assert_int_equal(range.min, TEST_2922_MIN_ID); + assert_int_equal(range.max, TEST_2922_MAX_ID); + assert_int_equal(slice_num, TEST_2922_DFL_SLIDE); + + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, name, sid, &range, + NULL, 0, false /* No external mapping */); + assert_int_equal(err, IDMAP_SUCCESS); + + return 0; +} + +static int test_sss_idmap_setup_with_domains(void **state) +{ + struct test_ctx *test_ctx; + + test_sss_idmap_setup(state); + + test_ctx = talloc_get_type(*state, struct test_ctx); + assert_non_null(test_ctx); + + setup_ranges(test_ctx, false, false, false); + return 0; +} + +static int test_sss_idmap_setup_with_domains_2922(void **state) +{ + struct test_ctx *test_ctx; + + test_sss_idmap_setup(state); + + test_ctx = talloc_get_type(*state, struct test_ctx); + assert_non_null(test_ctx); + + setup_ranges_2922(test_ctx); + return 0; +} + +static int test_sss_idmap_setup_with_domains_sec_slices(void **state) +{ + struct test_ctx *test_ctx; + + test_sss_idmap_setup(state); + + test_ctx = talloc_get_type(*state, struct test_ctx); + assert_non_null(test_ctx); + + setup_ranges(test_ctx, false, false, true); + return 0; +} + +static int test_sss_idmap_setup_with_external_mappings(void **state) +{ + struct test_ctx *test_ctx; + + test_sss_idmap_setup(state); + + test_ctx = talloc_get_type(*state, struct test_ctx); + assert_non_null(test_ctx); + + setup_ranges(test_ctx, true, false, false); + return 0; +} + +static int test_sss_idmap_setup_with_both(void **state) +{ + struct test_ctx *test_ctx; + + test_sss_idmap_setup(state); + + test_ctx = talloc_get_type(*state, struct test_ctx); + assert_non_null(test_ctx); + + setup_ranges(test_ctx, false, false, false); + setup_ranges(test_ctx, true, true, false); + return 0; +} + +static int test_sss_idmap_teardown(void **state) +{ + struct test_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + talloc_free(test_ctx->idmap_ctx); + talloc_free(test_ctx->mem_idmap); + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void test_add_domain(void **state) +{ + struct test_ctx *test_ctx; + enum idmap_error_code err; + struct sss_idmap_range range; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + range.min = TEST_RANGE_MIN; + range.max = TEST_RANGE_MAX; + + err = sss_idmap_add_domain(test_ctx->idmap_ctx, TEST_DOM_NAME, TEST_DOM_SID, + &range); + assert_int_equal(err, IDMAP_SUCCESS); + + err = sss_idmap_add_domain(test_ctx->idmap_ctx, TEST_DOM_NAME, TEST_DOM_SID, + &range); + assert_int_equal(err, IDMAP_COLLISION); + + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, TEST_DOM_NAME, + TEST_DOM_SID, &range, NULL, 0, false); + assert_int_equal(err, IDMAP_COLLISION); + + range.min = TEST_RANGE_MIN + TEST_OFFSET; + range.max = TEST_RANGE_MAX + TEST_OFFSET; + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, TEST_DOM_NAME, + TEST_DOM_SID, &range, NULL, 0, false); + assert_int_equal(err, IDMAP_COLLISION); + + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, TEST_DOM_NAME"X", + TEST_DOM_SID, &range, NULL, TEST_OFFSET, + false); + assert_int_equal(err, IDMAP_COLLISION); + + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, TEST_DOM_NAME, + TEST_DOM_SID"1", &range, NULL, TEST_OFFSET, + false); + assert_int_equal(err, IDMAP_COLLISION); + + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, TEST_DOM_NAME, + TEST_DOM_SID, &range, NULL, TEST_OFFSET, + true); + assert_int_equal(err, IDMAP_COLLISION); + + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, TEST_DOM_NAME, + TEST_DOM_SID, &range, NULL, TEST_OFFSET, + false); + assert_int_equal(err, IDMAP_SUCCESS); + + range.min = TEST_RANGE_MIN + 2 * TEST_OFFSET; + range.max = TEST_RANGE_MAX + 2 * TEST_OFFSET; + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, TEST_DOM_NAME"-nosid", + NULL, &range, NULL, TEST_OFFSET, + false); + assert_int_equal(err, IDMAP_SID_INVALID); + + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, TEST_DOM_NAME"-nosid", + NULL, &range, NULL, TEST_OFFSET, + true); + assert_int_equal(err, IDMAP_SUCCESS); +} + +void test_map_id(void **state) +{ + struct test_ctx *test_ctx; + enum idmap_error_code err; + uint32_t id; + char *sid = NULL; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"1-1", &id); + assert_int_equal(err, IDMAP_NO_DOMAIN); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-400000", + &id); + assert_int_equal(err, IDMAP_NO_RANGE); + + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, TEST_OFFSET - 1, &sid); + assert_int_equal(err, IDMAP_NO_DOMAIN); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-0", &id); + assert_int_equal(err, IDMAP_SUCCESS); + assert_int_equal(id, TEST_RANGE_MIN); + + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, id, &sid); + assert_int_equal(err, IDMAP_SUCCESS); + assert_string_equal(sid, TEST_DOM_SID"-0"); + sss_idmap_free_sid(test_ctx->idmap_ctx, sid); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, + TEST_DOM_SID"-"TEST_OFFSET_STR, &id); + assert_int_equal(err, IDMAP_SUCCESS); + assert_int_equal(id, TEST_RANGE_MIN+TEST_OFFSET); + + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, id, &sid); + assert_int_equal(err, IDMAP_SUCCESS); + assert_string_equal(sid, TEST_DOM_SID"-"TEST_OFFSET_STR); + sss_idmap_free_sid(test_ctx->idmap_ctx, sid); +} + +/* https://fedorahosted.org/sssd/ticket/2922 */ +/* ID mapping - bug in computing max id for slice range */ +void test_map_id_2922(void **state) +{ + const char* TEST_2922_FIRST_SID = TEST_DOM_SID"-0"; + /* Last SID = first SID + (default) rangesize -1 */ + const char* TEST_2922_LAST_SID = TEST_DOM_SID"-199999"; + /* Last SID = first SID + rangesize */ + const char* TEST_2922_LAST_SID_PLUS_ONE = TEST_DOM_SID"-200000"; + struct test_ctx *test_ctx; + enum idmap_error_code err; + uint32_t id; + char *sid = NULL; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + /* Min UNIX ID to SID */ + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, TEST_2922_MIN_ID, &sid); + assert_int_equal(err, IDMAP_SUCCESS); + assert_string_equal(sid, TEST_2922_FIRST_SID); + sss_idmap_free_sid(test_ctx->idmap_ctx, sid); + + /* First SID to UNIX ID */ + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_2922_FIRST_SID, &id); + assert_int_equal(err, IDMAP_SUCCESS); + assert_int_equal(id, TEST_2922_MIN_ID); + + /* Max UNIX ID to SID */ + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, TEST_2922_MAX_ID, &sid); + assert_int_equal(err, IDMAP_SUCCESS); + assert_string_equal(sid, TEST_2922_LAST_SID); + sss_idmap_free_sid(test_ctx->idmap_ctx, sid); + + /* Last SID to UNIX ID */ + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_2922_LAST_SID, &id); + assert_int_equal(err, IDMAP_SUCCESS); + assert_int_equal(id, TEST_2922_MAX_ID); + + /* Max UNIX ID + 1 to SID */ + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, TEST_2922_MAX_ID + 1, + &sid); + assert_int_equal(err, IDMAP_NO_DOMAIN); + + /* Last SID + 1 to UNIX ID */ + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, + TEST_2922_LAST_SID_PLUS_ONE, &id); + /* Auto adding new ranges is disable in this test. */ + assert_int_equal(err, IDMAP_NO_RANGE); +} + +void test_map_id_sec_slices(void **state) +{ + struct test_ctx *test_ctx; + enum idmap_error_code err; + uint32_t id; + char *sid = NULL; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"1-1", &id); + assert_int_equal(err, IDMAP_NO_DOMAIN); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-4000000", + &id); + assert_int_equal(err, IDMAP_SUCCESS); + assert_int_equal(id, 575600000); + + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, TEST_OFFSET - 1, &sid); + assert_int_equal(err, IDMAP_NO_DOMAIN); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-0", &id); + assert_int_equal(err, IDMAP_SUCCESS); + assert_int_equal(id, TEST_RANGE_MIN); + + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, id, &sid); + assert_int_equal(err, IDMAP_SUCCESS); + assert_string_equal(sid, TEST_DOM_SID"-0"); + sss_idmap_free_sid(test_ctx->idmap_ctx, sid); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, + TEST_DOM_SID"-"TEST_OFFSET_STR, &id); + assert_int_equal(err, IDMAP_SUCCESS); + assert_int_equal(id, TEST_RANGE_MIN+TEST_OFFSET); + + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, id, &sid); + assert_int_equal(err, IDMAP_SUCCESS); + assert_string_equal(sid, TEST_DOM_SID"-"TEST_OFFSET_STR); + sss_idmap_free_sid(test_ctx->idmap_ctx, sid); +} + +void test_map_id_external(void **state) +{ + struct test_ctx *test_ctx; + enum idmap_error_code err; + uint32_t id; + char *sid = NULL; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"1-1", &id); + assert_int_equal(err, IDMAP_NO_DOMAIN); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-400000", + &id); + assert_int_equal(err, IDMAP_EXTERNAL); + + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, TEST_OFFSET - 1, &sid); + assert_int_equal(err, IDMAP_NO_DOMAIN); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-0", &id); + assert_int_equal(err, IDMAP_EXTERNAL); + + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, TEST_RANGE_MIN, &sid); + assert_int_equal(err, IDMAP_EXTERNAL); + + err = sss_idmap_sid_to_unix(test_ctx->idmap_ctx, + TEST_DOM_SID"-"TEST_OFFSET_STR, &id); + assert_int_equal(err, IDMAP_EXTERNAL); + + err = sss_idmap_unix_to_sid(test_ctx->idmap_ctx, + TEST_RANGE_MIN + TEST_OFFSET, &sid); + assert_int_equal(err, IDMAP_EXTERNAL); +} + +void test_check_sid_id(void **state) +{ + struct test_ctx *test_ctx; + enum idmap_error_code err; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + err = sss_idmap_check_sid_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-400000", + TEST_RANGE_MIN-1); + assert_int_equal(err, IDMAP_NO_RANGE); + + err = sss_idmap_check_sid_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-400000", + TEST_RANGE_MIN); + assert_int_equal(err, IDMAP_SUCCESS); + + err = sss_idmap_check_sid_unix(test_ctx->idmap_ctx, TEST_DOM_SID"1-400000", + TEST_RANGE_MIN); + assert_int_equal(err, IDMAP_SID_UNKNOWN); + + err = sss_idmap_check_sid_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-400000", + TEST_RANGE_MAX + TEST_OFFSET); + assert_int_equal(err, IDMAP_SUCCESS); + + err = sss_idmap_check_sid_unix(test_ctx->idmap_ctx, TEST_DOM_SID"-400000", + TEST_RANGE_MAX + TEST_OFFSET + 1); + assert_int_equal(err, IDMAP_NO_RANGE); +} + +void test_has_algorithmic(void **state) +{ + struct test_ctx *test_ctx; + bool use_id_mapping; + enum idmap_error_code err; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + err = sss_idmap_domain_has_algorithmic_mapping(NULL, NULL, &use_id_mapping); + assert_int_equal(err, IDMAP_SID_INVALID); + + err = sss_idmap_domain_has_algorithmic_mapping(NULL, TEST_DOM_SID, + &use_id_mapping); + assert_int_equal(err, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_domain_has_algorithmic_mapping(test_ctx->idmap_ctx, NULL, + &use_id_mapping); + assert_int_equal(err, IDMAP_SID_INVALID); + + err = sss_idmap_domain_has_algorithmic_mapping(test_ctx->idmap_ctx, + TEST_DOM_SID"1", + &use_id_mapping); + assert_int_equal(err, IDMAP_SID_UNKNOWN); + + err = sss_idmap_domain_has_algorithmic_mapping(test_ctx->idmap_ctx, + TEST_DOM_SID, + &use_id_mapping); + assert_int_equal(err, IDMAP_SUCCESS); + assert_true(use_id_mapping); + + err = sss_idmap_domain_has_algorithmic_mapping(test_ctx->idmap_ctx, + TEST_2_DOM_SID, + &use_id_mapping); + assert_int_equal(err, IDMAP_SUCCESS); + assert_false(use_id_mapping); +} + +void test_has_algorithmic_by_name(void **state) +{ + struct test_ctx *test_ctx; + bool use_id_mapping; + enum idmap_error_code err; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + err = sss_idmap_domain_by_name_has_algorithmic_mapping(NULL, NULL, &use_id_mapping); + assert_int_equal(err, IDMAP_ERROR); + + err = sss_idmap_domain_by_name_has_algorithmic_mapping(NULL, TEST_DOM_SID, + &use_id_mapping); + assert_int_equal(err, IDMAP_CONTEXT_INVALID); + + err = sss_idmap_domain_by_name_has_algorithmic_mapping(test_ctx->idmap_ctx, NULL, + &use_id_mapping); + assert_int_equal(err, IDMAP_ERROR); + + err = sss_idmap_domain_by_name_has_algorithmic_mapping(test_ctx->idmap_ctx, + TEST_DOM_NAME"1", + &use_id_mapping); + assert_int_equal(err, IDMAP_NAME_UNKNOWN); + + err = sss_idmap_domain_by_name_has_algorithmic_mapping(test_ctx->idmap_ctx, + TEST_DOM_NAME, + &use_id_mapping); + assert_int_equal(err, IDMAP_SUCCESS); + assert_true(use_id_mapping); + + err = sss_idmap_domain_by_name_has_algorithmic_mapping(test_ctx->idmap_ctx, + TEST_2_DOM_NAME, + &use_id_mapping); + assert_int_equal(err, IDMAP_SUCCESS); + assert_false(use_id_mapping); +} + +void test_sss_idmap_check_collision_ex(void **state) +{ + enum idmap_error_code err; + struct sss_idmap_range r1 = {TEST_RANGE_MIN, TEST_RANGE_MAX}; + struct sss_idmap_range r2 = {TEST_2_RANGE_MIN, TEST_2_RANGE_MAX}; + + err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1, + TEST_FIRST_RID, NULL, + TEST_EXT_MAPPING, + TEST_2_DOM_NAME, TEST_2_DOM_SID, &r2, + TEST_2_FIRST_RID, NULL, + TEST_2_EXT_MAPPING); + assert_int_equal(err, IDMAP_SUCCESS); + + /* Same name, different SID */ + err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1, + TEST_FIRST_RID, NULL, + TEST_EXT_MAPPING, + TEST_DOM_NAME, TEST_2_DOM_SID, &r2, + TEST_2_FIRST_RID, NULL, + TEST_2_EXT_MAPPING); + assert_int_equal(err, IDMAP_COLLISION); + + /* Same SID, different name */ + err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1, + TEST_FIRST_RID, NULL, + TEST_EXT_MAPPING, + TEST_2_DOM_NAME, TEST_DOM_SID, &r2, + TEST_2_FIRST_RID, NULL, + TEST_2_EXT_MAPPING); + assert_int_equal(err, IDMAP_COLLISION); + + /* Same SID and name, no overlaps */ + err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1, + TEST_FIRST_RID, NULL, + TEST_EXT_MAPPING, + TEST_DOM_NAME, TEST_DOM_SID, &r2, + TEST_2_FIRST_RID, NULL, + TEST_2_EXT_MAPPING); + assert_int_equal(err, IDMAP_SUCCESS); + + /* Same SID and name, different mappings */ + err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1, + TEST_FIRST_RID, NULL, + TEST_EXT_MAPPING, + TEST_DOM_NAME, TEST_DOM_SID, &r2, + TEST_2_FIRST_RID, NULL, + !TEST_EXT_MAPPING); + assert_int_equal(err, IDMAP_COLLISION); + + /* Same SID and name, Overlapping RID range */ + err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1, + TEST_FIRST_RID, NULL, + false, + TEST_DOM_NAME, TEST_DOM_SID, &r2, + TEST_FIRST_RID, NULL, + false); + assert_int_equal(err, IDMAP_COLLISION); + + /* Different SID and name, Overlapping RID range */ + err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1, + TEST_FIRST_RID, NULL, + false, + TEST_2_DOM_NAME, TEST_2_DOM_SID, &r2, + TEST_FIRST_RID, NULL, + false); + assert_int_equal(err, IDMAP_SUCCESS); + + + /* Overlapping ranges with no external mapping */ + err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1, + TEST_FIRST_RID, NULL, + false, + TEST_2_DOM_NAME, TEST_2_DOM_SID, &r1, + TEST_2_FIRST_RID, NULL, + false); + assert_int_equal(err, IDMAP_COLLISION); + + /* Overlapping ranges with external mapping */ + err = sss_idmap_check_collision_ex(TEST_DOM_NAME, TEST_DOM_SID, &r1, + TEST_FIRST_RID, NULL, + true, + TEST_2_DOM_NAME, TEST_2_DOM_SID, &r1, + TEST_2_FIRST_RID, NULL, + true); + assert_int_equal(err, IDMAP_SUCCESS); +} + +void test_sss_idmap_error_string(void **state) +{ + size_t c; + + for (c = IDMAP_SUCCESS; c < IDMAP_ERR_LAST; c++) { + assert_string_not_equal(idmap_error_string(c), + idmap_error_string(IDMAP_ERR_LAST)); + } +} + +void test_sss_idmap_calculate_range_slice_collision(void **state) +{ + struct test_ctx *test_ctx; + enum idmap_error_code err; + struct sss_idmap_range range; + id_t slice_num = 123; + + test_ctx = talloc_get_type(*state, struct test_ctx); + + assert_non_null(test_ctx); + + err = sss_idmap_calculate_range(test_ctx->idmap_ctx, NULL, &slice_num, + &range); + assert_int_equal(err, IDMAP_SUCCESS); + + err = sss_idmap_add_domain_ex(test_ctx->idmap_ctx, TEST_DOM_NAME, + TEST_DOM_SID, &range, NULL, 0, false); + assert_int_equal(err, IDMAP_SUCCESS); + + err = sss_idmap_calculate_range(test_ctx->idmap_ctx, NULL, &slice_num, + &range); + assert_int_equal(err, IDMAP_COLLISION); + + slice_num++; + err = sss_idmap_calculate_range(test_ctx->idmap_ctx, NULL, &slice_num, + &range); + assert_int_equal(err, IDMAP_SUCCESS); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_add_domain, + test_sss_idmap_setup, + test_sss_idmap_teardown), + cmocka_unit_test_setup_teardown(test_map_id, + test_sss_idmap_setup_with_domains, + test_sss_idmap_teardown), + cmocka_unit_test_setup_teardown(test_map_id_2922, + test_sss_idmap_setup_with_domains_2922, + test_sss_idmap_teardown), + cmocka_unit_test_setup_teardown(test_map_id_sec_slices, + test_sss_idmap_setup_with_domains_sec_slices, + test_sss_idmap_teardown), + cmocka_unit_test_setup_teardown(test_map_id_external, + test_sss_idmap_setup_with_external_mappings, + test_sss_idmap_teardown), + cmocka_unit_test_setup_teardown(test_check_sid_id, + test_sss_idmap_setup_with_domains, + test_sss_idmap_teardown), + cmocka_unit_test_setup_teardown(test_check_sid_id, + test_sss_idmap_setup_with_external_mappings, + test_sss_idmap_teardown), + cmocka_unit_test_setup_teardown(test_has_algorithmic, + test_sss_idmap_setup_with_both, + test_sss_idmap_teardown), + cmocka_unit_test_setup_teardown(test_has_algorithmic_by_name, + test_sss_idmap_setup_with_both, + test_sss_idmap_teardown), + cmocka_unit_test(test_sss_idmap_check_collision_ex), + cmocka_unit_test(test_sss_idmap_error_string), + cmocka_unit_test_setup_teardown(test_sss_idmap_calculate_range_slice_collision, + test_sss_idmap_setup, + test_sss_idmap_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_sss_ptr_hash.c b/src/tests/cmocka/test_sss_ptr_hash.c new file mode 100644 index 0000000..31cf8b7 --- /dev/null +++ b/src/tests/cmocka/test_sss_ptr_hash.c @@ -0,0 +1,232 @@ +/* + Copyright (C) 2020 Red Hat + + 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 "tests/cmocka/common_mock.h" +#include "util/sss_ptr_hash.h" + +static const int MAX_ENTRIES_AMOUNT = 5; + +static void populate_table(hash_table_t *table, int **payloads) +{ + char key[2] = {'z', 0}; + + for (int i = 0; i < MAX_ENTRIES_AMOUNT; ++i) { + payloads[i] = talloc_zero(global_talloc_context, int); + assert_non_null(payloads[i]); + *payloads[i] = i; + key[0] = '0'+(char)i; + assert_int_equal(sss_ptr_hash_add(table, key, payloads[i], int), 0); + } + + assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT); +} + +static void free_payload_cb(hash_entry_t *item, hash_destroy_enum type, void *pvt) +{ + int *counter; + + assert_non_null(item); + assert_non_null(item->value.ptr); + talloc_zfree(item->value.ptr); + + assert_non_null(pvt); + counter = (int *)pvt; + (*counter)++; +} + +void test_sss_ptr_hash_with_free_cb(void **state) +{ + hash_table_t *table; + int free_counter = 0; + int *payloads[MAX_ENTRIES_AMOUNT]; + + table = sss_ptr_hash_create(global_talloc_context, + free_payload_cb, + &free_counter); + assert_non_null(table); + + populate_table(table, payloads); + + /* check explicit removal from the hash */ + sss_ptr_hash_delete(table, "1", false); + assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-1); + assert_int_equal(free_counter, 1); + + /* check implicit removal triggered by payload deletion */ + talloc_free(payloads[3]); + assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-2); + assert_int_equal(free_counter, 2); + + /* try to remove non existent entry */ + sss_ptr_hash_delete(table, "q", false); + assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-2); + assert_int_equal(free_counter, 2); + + /* clear all */ + sss_ptr_hash_delete_all(table, false); + assert_int_equal((int)hash_count(table), 0); + assert_int_equal(free_counter, MAX_ENTRIES_AMOUNT); + + /* check that table is still operable */ + populate_table(table, payloads); + sss_ptr_hash_delete(table, "2", false); + assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-1); + assert_int_equal(free_counter, MAX_ENTRIES_AMOUNT+1); + + talloc_free(table); + assert_int_equal(free_counter, MAX_ENTRIES_AMOUNT*2); +} + +void test_sss_ptr_hash_overwrite_with_free_cb(void **state) +{ + hash_table_t *table; + int free_counter = 0; + unsigned long count; + char *payload; + char *value; + errno_t ret; + + table = sss_ptr_hash_create(global_talloc_context, + free_payload_cb, + &free_counter); + assert_non_null(table); + + payload = talloc_strdup(table, "test_value1"); + assert_non_null(payload); + talloc_set_name_const(payload, "char"); + ret = sss_ptr_hash_add_or_override(table, "test", payload, char); + assert_int_equal(ret, 0); + count = hash_count(table); + assert_int_equal(count, 1); + value = sss_ptr_hash_lookup(table, "test", char); + assert_ptr_equal(value, payload); + + + payload = talloc_strdup(table, "test_value2"); + assert_non_null(payload); + talloc_set_name_const(payload, "char"); + ret = sss_ptr_hash_add_or_override(table, "test", payload, char); + assert_int_equal(ret, 0); + count = hash_count(table); + assert_int_equal(count, 1); + value = sss_ptr_hash_lookup(table, "test", char); + assert_ptr_equal(value, payload); + + talloc_free(table); + assert_int_equal(free_counter, 2); +} + +struct table_wrapper +{ + hash_table_t **table; +}; + +static void lookup_cb(hash_entry_t *item, hash_destroy_enum type, void *pvt) +{ + hash_table_t *table; + hash_key_t *keys; + unsigned long count; + int *value = NULL; + int sum = 0; + + assert_non_null(pvt); + table = *((struct table_wrapper *)pvt)->table; + assert_non_null(table); + + if (type == HASH_TABLE_DESTROY) { + /* table is being destroyed */ + return; + } + + assert_int_equal(hash_keys(table, &count, &keys), HASH_SUCCESS); + for (unsigned int i = 0; i < count; ++i) { + assert_int_equal(keys[i].type, HASH_KEY_STRING); + value = sss_ptr_hash_lookup(table, keys[i].c_str, int); + assert_non_null(value); + sum += *value; + } + DEBUG(SSSDBG_TRACE_ALL, "sum of all values = %d\n", sum); + talloc_free(keys); +} + +/* main difference with `test_sss_ptr_hash_with_free_cb()` + * is that table cb here doesn't delete payload so + * this is requested via `free_value(s)` arg + */ +void test_sss_ptr_hash_with_lookup_cb(void **state) +{ + hash_table_t *table; + struct table_wrapper wrapper; + int *payloads[MAX_ENTRIES_AMOUNT]; + + wrapper.table = &table; + table = sss_ptr_hash_create(global_talloc_context, + lookup_cb, + &wrapper); + assert_non_null(table); + + populate_table(table, payloads); + + /* check explicit removal from the hash */ + sss_ptr_hash_delete(table, "2", true); + assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-1); + + /* check implicit removal triggered by payload deletion */ + talloc_free(payloads[0]); + assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-2); + + /* clear all */ + sss_ptr_hash_delete_all(table, true); + assert_int_equal((int)hash_count(table), 0); + /* teardown function shall verify there are no leaks + * on global_talloc_context and so that payloads[] were freed + */ + + /* check that table is still operable */ + populate_table(table, payloads); + + talloc_free(table); + /* d-tor triggers hash_destroy() but since cb here doesn free payload + * this should be done manually + */ + for (int i = 0; i < MAX_ENTRIES_AMOUNT; ++i) { + talloc_free(payloads[i]); + } +} + +/* Just smoke test to verify that absence of cb doesn't break anything */ +void test_sss_ptr_hash_without_cb(void **state) +{ + hash_table_t *table; + int *payloads[MAX_ENTRIES_AMOUNT]; + + table = sss_ptr_hash_create(global_talloc_context, NULL, NULL); + assert_non_null(table); + + populate_table(table, payloads); + + sss_ptr_hash_delete(table, "4", true); + assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-1); + + talloc_free(payloads[1]); + assert_int_equal((int)hash_count(table), MAX_ENTRIES_AMOUNT-2); + + sss_ptr_hash_delete_all(table, true); + assert_int_equal((int)hash_count(table), 0); + + talloc_free(table); +} diff --git a/src/tests/cmocka/test_sss_sifp.c b/src/tests/cmocka/test_sss_sifp.c new file mode 100644 index 0000000..23d27b6 --- /dev/null +++ b/src/tests/cmocka/test_sss_sifp.c @@ -0,0 +1,2250 @@ +/* + Authors: + Pavel Bナ册zina <pbrezina@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 <dbus/dbus.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "lib/sifp/sss_sifp.h" +#include "lib/sifp/sss_sifp_dbus.h" +#include "lib/sifp/sss_sifp_private.h" +#include "responder/ifp/ifp_iface/ifp_iface.h" + +struct { + sss_sifp_ctx *dbus_ctx; + DBusMessage *reply; +} test_ctx; + +DBusConnection * +__wrap_dbus_bus_get(DBusBusType type, DBusError *error) +{ + /* we won't use the connection anywhere, so we can just return NULL */ + return NULL; +} + +DBusMessage * +__wrap_dbus_connection_send_with_reply_and_block(DBusConnection *connection, + DBusMessage *message, + int timeout_milliseconds, + DBusError *error) +{ + if (message == NULL || error == NULL) { + return NULL; + } + + return sss_mock_ptr_type(DBusMessage *); +} + +static void reply_variant_basic(DBusMessage *reply, + const char *type, + const void *val) +{ + DBusMessageIter iter; + DBusMessageIter variant_iter; + dbus_bool_t bret; + + dbus_message_iter_init_append(reply, &iter); + + + bret = dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + type, &variant_iter); + assert_true(bret); + + /* Now add the value */ + bret = dbus_message_iter_append_basic(&variant_iter, type[0], val); + assert_true(bret); + + bret = dbus_message_iter_close_container(&iter, &variant_iter); + assert_true(bret); +} + +static void reply_variant_array(DBusMessage *reply, + const char *type, + int num_vals, + uint8_t *vals, + unsigned int item_size) +{ + DBusMessageIter iter; + DBusMessageIter variant_iter; + DBusMessageIter array_iter; + dbus_bool_t bret; + char array_type[3]; + int i; + void *addr; + + array_type[0] = DBUS_TYPE_ARRAY; + array_type[1] = type[0]; + array_type[2] = '\0'; + + dbus_message_iter_init_append(reply, &iter); + + + bret = dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + array_type, &variant_iter); + assert_true(bret); + + bret = dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + type, &array_iter); + assert_true(bret); + + for (i = 0; i < num_vals; i++) { + addr = vals + i*item_size; + bret = dbus_message_iter_append_basic(&array_iter, type[0], addr); + assert_true(bret); + } + + bret = dbus_message_iter_close_container(&iter, &array_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&iter, &variant_iter); + assert_true(bret); +} + +static int test_setup(void **state) +{ + sss_sifp_error ret; + + ret = sss_sifp_init(&test_ctx.dbus_ctx); + assert_int_equal(ret, SSS_SIFP_OK); + + test_ctx.reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + assert_non_null(test_ctx.reply); + return 0; +} + +static int test_teardown_parser(void **state) +{ + sss_sifp_free(&test_ctx.dbus_ctx); + assert_null(test_ctx.dbus_ctx); + + dbus_message_unref(test_ctx.reply); + test_ctx.reply = NULL; + return 0; +} + +static int test_teardown_api(void **state) +{ + sss_sifp_free(&test_ctx.dbus_ctx); + assert_null(test_ctx.dbus_ctx); + + /* sss_sifp is responsible for freeing the reply */ + return 0; +} + +void test_sss_sifp_strdup_valid(void **state) +{ + const char *str = "test_string"; + char *dup_str = sss_sifp_strdup(test_ctx.dbus_ctx, str); + assert_non_null(dup_str); + assert_string_equal(str, dup_str); + + sss_sifp_free_string(test_ctx.dbus_ctx, &dup_str); + assert_null(dup_str); +} + +void test_sss_sifp_strdup_null(void **state) +{ + char *dup_str = sss_sifp_strdup(test_ctx.dbus_ctx, NULL); + assert_null(dup_str); +} + +void test_sss_sifp_strcat_valid(void **state) +{ + char *cat = sss_sifp_strcat(test_ctx.dbus_ctx, "hello ", "world"); + assert_non_null(cat); + assert_string_equal("hello world", cat); + + sss_sifp_free_string(test_ctx.dbus_ctx, &cat); + assert_null(cat); +} + +void test_sss_sifp_strcat_left_null(void **state) +{ + char *cat = sss_sifp_strcat(test_ctx.dbus_ctx, NULL, "world"); + assert_non_null(cat); + assert_string_equal("world", cat); + + sss_sifp_free_string(test_ctx.dbus_ctx, &cat); + assert_null(cat); +} + +void test_sss_sifp_strcat_right_null(void **state) +{ + char *cat = sss_sifp_strcat(test_ctx.dbus_ctx, "hello ", NULL); + assert_non_null(cat); + assert_string_equal("hello ", cat); + + sss_sifp_free_string(test_ctx.dbus_ctx, &cat); + assert_null(cat); +} + +void test_sss_sifp_strcat_both_null(void **state) +{ + char *cat = sss_sifp_strcat(test_ctx.dbus_ctx, NULL, NULL); + assert_null(cat); +} + +void test_sss_sifp_parse_object_path_valid(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + dbus_bool_t bret; + sss_sifp_error ret; + const char *path_in = "/object/path"; + char *path_out = NULL; + + /* prepare message */ + bret = dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path_in, + DBUS_TYPE_INVALID); + assert_true(bret); + + /* test */ + ret = sss_sifp_parse_object_path(ctx, reply, &path_out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(path_out); + assert_string_equal(path_in, path_out); + + sss_sifp_free_string(ctx, &path_out); + assert_null(path_out); +} + +void test_sss_sifp_parse_object_path_invalid(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + dbus_bool_t bret; + sss_sifp_error ret; + uint16_t path_in = 10; + char *path_out = NULL; + + /* prepare message */ + bret = dbus_message_append_args(reply, DBUS_TYPE_UINT16, &path_in, + DBUS_TYPE_INVALID); + assert_true(bret); + + /* test */ + ret = sss_sifp_parse_object_path(ctx, reply, &path_out); + assert_int_not_equal(ret, SSS_SIFP_OK); + assert_null(path_out); +} + +void test_sss_sifp_parse_object_path_list_valid(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + dbus_bool_t bret; + sss_sifp_error ret; + char **path_out = NULL; + const char *path_in[] = {"/object/path1", "/object/path2"}; + const char **paths = path_in; + int path_in_len = 2; + int i; + + /* prepare message */ + bret = dbus_message_append_args(reply, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH, + &paths, path_in_len, + DBUS_TYPE_INVALID); + assert_true(bret); + + /* test */ + ret = sss_sifp_parse_object_path_list(ctx, reply, &path_out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(path_out); + + for (i = 0; path_out[i] != NULL; i++) { + assert_true(i < path_in_len); + assert_non_null(path_out[i]); + assert_string_equal(path_in[i], path_out[i]); + } + + sss_sifp_free_string_array(ctx, &path_out); + assert_null(path_out); +} + +void test_sss_sifp_parse_object_path_list_invalid(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + dbus_bool_t bret; + sss_sifp_error ret; + char **path_out = NULL; + const char *path_in = "/object/path"; + + /* prepare message */ + bret = dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path_in, + DBUS_TYPE_INVALID); + assert_true(bret); + + /* test */ + ret = sss_sifp_parse_object_path_list(ctx, reply, &path_out); + assert_int_not_equal(ret, SSS_SIFP_OK); + assert_null(path_out); +} + +void test_sss_sifp_parse_attr_bool(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + dbus_bool_t in = 1; + bool out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_BOOLEAN_AS_STRING, &in); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_BOOL); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_bool(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_true(in == out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_int16(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + int16_t in = INT16_MIN; + int16_t out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_INT16_AS_STRING, &in); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_INT16); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_int16(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(in, out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_uint16(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + uint16_t in = UINT16_MAX; + uint16_t out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_UINT16_AS_STRING, &in); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT16); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint16(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(in, out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_int32(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + int32_t in = INT32_MIN; + int32_t out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_INT32_AS_STRING, &in); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_INT32); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_int32(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(in, out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_uint32(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + uint32_t in = UINT32_MAX; + uint32_t out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_UINT32_AS_STRING, &in); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT32); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint32(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(in, out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_int64(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + int64_t in = INT64_MIN; + int64_t out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_INT64_AS_STRING, &in); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_INT64); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_int64(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(in, out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_uint64(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + uint64_t in = UINT64_MAX; + uint64_t out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_UINT64_AS_STRING, &in); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT64); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint64(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(in, out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_string(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + const char *in = "test value"; + const char *out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_STRING_AS_STRING, &in); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_STRING); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_string(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_string_equal(in, out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_object_path(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + const char *in = "/object/path"; + const char *out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_OBJECT_PATH_AS_STRING, &in); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_STRING); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_string(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_string_equal(in, out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_string_dict(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + DBusMessageIter iter; + DBusMessageIter var_iter; + DBusMessageIter array_iter; + DBusMessageIter dict_iter; + dbus_bool_t bret; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + struct { + const char *key; + const char *value; + } data = {"key", "value"}; + hash_table_t *out; + hash_key_t key; + hash_value_t value; + char **values; + int hret; + + /* prepare message */ + dbus_message_iter_init_append(reply, &iter); + + bret = dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &var_iter); + assert_true(bret); + + bret = dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &array_iter); + assert_true(bret); + + bret = dbus_message_iter_open_container(&array_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &dict_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, + &data.key); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, + &data.value); + assert_true(bret); + + bret = dbus_message_iter_close_container(&array_iter, &dict_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&var_iter, &array_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&iter, &var_iter); + assert_true(bret); + + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_STRING_DICT); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_string_dict(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(hash_count(out), 1); + + key.type = HASH_KEY_STRING; + key.str = discard_const(data.key); + hret = hash_lookup(out, &key, &value); + assert_int_equal(hret, HASH_SUCCESS); + assert_int_equal(value.type, HASH_VALUE_PTR); + assert_non_null(value.ptr); + values = value.ptr; + assert_non_null(values[0]); + assert_string_equal(values[0], data.value); + assert_null(values[1]); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_bool_array(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 5; + dbus_bool_t in_array[] = {1, 1, 0, 0, 1}; + dbus_bool_t *in = in_array; + unsigned int out_num; + bool *out; + unsigned int i; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_BOOLEAN_AS_STRING, num_values, + (uint8_t*)in, sizeof(dbus_bool_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_BOOL); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_bool_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(num_values, out_num); + + for (i = 0; i < num_values; i++) { + assert_true(in[i] == out[i]); + } + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_bool_array_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 0; + unsigned int out_num; + bool *out; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_BOOLEAN_AS_STRING, num_values, + NULL, sizeof(dbus_bool_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_BOOL); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_bool_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_ATTR_NULL); + assert_int_equal(num_values, out_num); + assert_null(out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_int16_array(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 5; + int16_t in_array[] = {10, 15, -10, -15, 5559}; + int16_t *in = in_array; + unsigned int out_num; + int16_t *out; + unsigned int i; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_INT16_AS_STRING, num_values, + (uint8_t*)in, sizeof(int16_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_INT16); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_int16_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(num_values, out_num); + + for (i = 0; i < num_values; i++) { + assert_int_equal(in[i], out[i]); + } + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_int16_array_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 0; + unsigned int out_num; + int16_t *out; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_INT16_AS_STRING, num_values, + NULL, sizeof(int16_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_INT16); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_int16_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_ATTR_NULL); + assert_int_equal(num_values, out_num); + assert_null(out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_uint16_array(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 5; + uint16_t in_array[] = {10, 15, 8885, 3224, 5559}; + uint16_t *in = in_array; + unsigned int out_num; + uint16_t *out; + unsigned int i; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_UINT16_AS_STRING, num_values, + (uint8_t*)in, sizeof(uint16_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT16); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint16_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(num_values, out_num); + + for (i = 0; i < num_values; i++) { + assert_int_equal(in[i], out[i]); + } + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_uint16_array_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 0; + unsigned int out_num; + uint16_t *out; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_UINT16_AS_STRING, num_values, + NULL, sizeof(uint16_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT16); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint16_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_ATTR_NULL); + assert_int_equal(num_values, out_num); + assert_null(out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_int32_array(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 5; + int32_t in_array[] = {10, 15, -10, -15, 5559}; + int32_t *in = in_array; + unsigned int out_num; + int32_t *out; + unsigned int i; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_INT32_AS_STRING, num_values, + (uint8_t*)in, sizeof(int32_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_INT32); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_int32_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(num_values, out_num); + + for (i = 0; i < num_values; i++) { + assert_int_equal(in[i], out[i]); + } + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_int32_array_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 0; + unsigned int out_num; + int32_t *out; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_INT32_AS_STRING, num_values, + NULL, sizeof(int32_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_INT32); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_int32_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_ATTR_NULL); + assert_int_equal(num_values, out_num); + assert_null(out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_uint32_array(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 5; + uint32_t in_array[] = {10, 15, 8885, 3224, 5559}; + uint32_t *in = in_array; + unsigned int out_num; + uint32_t *out; + unsigned int i; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_UINT32_AS_STRING, num_values, + (uint8_t*)in, sizeof(uint32_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT32); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint32_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(num_values, out_num); + + for (i = 0; i < num_values; i++) { + assert_int_equal(in[i], out[i]); + } + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_uint32_array_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 0; + unsigned int out_num; + uint32_t *out; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_UINT32_AS_STRING, num_values, + NULL, sizeof(uint32_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT32); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint32_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_ATTR_NULL); + assert_int_equal(num_values, out_num); + assert_null(out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_int64_array(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 5; + int64_t in_array[] = {10, 15, -10, -15, 5559}; + int64_t *in = in_array; + unsigned int out_num; + int64_t *out; + unsigned int i; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_INT64_AS_STRING, num_values, + (uint8_t*)in, sizeof(int64_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_INT64); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_int64_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(num_values, out_num); + + for (i = 0; i < num_values; i++) { + assert_int_equal(in[i], out[i]); + } + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_int64_array_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 0; + unsigned int out_num; + int64_t *out; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_INT64_AS_STRING, num_values, + NULL, sizeof(int64_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_INT64); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_int64_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_ATTR_NULL); + assert_int_equal(num_values, out_num); + assert_null(out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_uint64_array(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 5; + uint64_t in_array[] = {10, 15, 8885, 3224, 5559}; + uint64_t *in = in_array; + unsigned int out_num; + uint64_t *out; + unsigned int i; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_UINT64_AS_STRING, num_values, + (uint8_t*)in, sizeof(uint64_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT64); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint64_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(num_values, out_num); + + for (i = 0; i < num_values; i++) { + assert_int_equal(in[i], out[i]); + } + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_uint64_array_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 0; + unsigned int out_num; + uint64_t *out; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_UINT64_AS_STRING, num_values, + NULL, sizeof(uint64_t)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT64); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint64_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_ATTR_NULL); + assert_int_equal(num_values, out_num); + assert_null(out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_string_array(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 6; + const char *in_array[] = {"I", "don't", "like", "writing", "unit", "tests"}; + const char **in = in_array; + unsigned int out_num; + const char * const *out; + unsigned int i; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_STRING_AS_STRING, num_values, + (uint8_t*)in, sizeof(const char*)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_STRING); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_string_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(num_values, out_num); + + for (i = 0; i < num_values; i++) { + assert_string_equal(in[i], out[i]); + } + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_string_array_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 0; + unsigned int out_num; + const char * const *out; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_STRING_AS_STRING, num_values, + NULL, sizeof(const char*)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_STRING); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_string_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_ATTR_NULL); + assert_int_equal(num_values, out_num); + assert_null(out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_object_path_array(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 2; + const char *in_array[] = {"/object/path1", "/object/path2"}; + const char **in = in_array; + unsigned int out_num; + const char * const *out; + unsigned int i; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_OBJECT_PATH_AS_STRING, num_values, + (uint8_t*)in, sizeof(const char*)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_STRING); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_string_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(num_values, out_num); + + for (i = 0; i < num_values; i++) { + assert_string_equal(in[i], out[i]); + } + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_object_path_array_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + unsigned int num_values = 0; + unsigned int out_num; + const char * const *out; + + /* prepare message */ + reply_variant_array(reply, DBUS_TYPE_OBJECT_PATH_AS_STRING, num_values, + NULL, sizeof(const char*)); + + /* test */ + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, num_values); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_STRING); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_string_array(attrs, name, &out_num, &out); + assert_int_equal(ret, SSS_SIFP_ATTR_NULL); + assert_int_equal(num_values, out_num); + assert_null(out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_string_dict_array(void **state) +{ + return; + + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + DBusMessageIter iter; + DBusMessageIter var_iter; + DBusMessageIter array_iter; + DBusMessageIter dict_iter; + DBusMessageIter val_iter; + dbus_bool_t bret; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + static struct { + const char *key; + const char *values[]; + } data = {"key", {"value1", "value2", "value3"}}; + unsigned int num_values = 3; + hash_table_t *out; + hash_key_t key; + hash_value_t value; + char **values; + unsigned int i; + int hret; + + /* prepare message */ + dbus_message_iter_init_append(reply, &iter); + + bret = dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &var_iter); + assert_true(bret); + + bret = dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &array_iter); + assert_true(bret); + + bret = dbus_message_iter_open_container(&array_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &dict_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, + &data.key); + assert_true(bret); + + bret = dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, + &val_iter); + assert_true(bret); + + for (i = 0; i < num_values; i++) { + bret = dbus_message_iter_append_basic(&val_iter, DBUS_TYPE_STRING, + &data.values[i]); + assert_true(bret); + } + + bret = dbus_message_iter_close_container(&dict_iter, &val_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&array_iter, &dict_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&var_iter, &array_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&iter, &var_iter); + assert_true(bret); + + ret = sss_sifp_parse_attr(ctx, name, reply, &attrs); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_STRING_DICT); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_string_dict(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(hash_count(out), 1); + + key.type = HASH_KEY_STRING; + key.str = discard_const(data.key); + hret = hash_lookup(out, &key, &value); + assert_int_equal(hret, HASH_SUCCESS); + assert_int_equal(value.type, HASH_VALUE_PTR); + assert_non_null(value.ptr); + values = value.ptr; + + for (i = 0; i < num_values; i++) { + assert_non_null(values[i]); + assert_string_equal(values[i], data.values[i]); + } + assert_null(values[i]); + + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_list(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + DBusMessageIter iter; + DBusMessageIter array_iter; + DBusMessageIter dict_iter; + DBusMessageIter var_iter; + dbus_bool_t bret; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + struct { + const char *name; + uint32_t value; + } data[] = {{"attr1", 1}, {"attr2", 2}, {"attr3", 3}, {NULL, 0}}; + uint32_t out; + int i; + + /* prepare message */ + dbus_message_iter_init_append(reply, &iter); + + bret = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &array_iter); + assert_true(bret); + + for (i = 0; data[i].name != NULL; i++) { + bret = dbus_message_iter_open_container(&array_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &dict_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, + &data[i].name); + assert_true(bret); + + bret = dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_UINT32_AS_STRING, + &var_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_UINT32, + &data[i].value); + assert_true(bret); + + bret = dbus_message_iter_close_container(&dict_iter, &var_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&array_iter, &dict_iter); + assert_true(bret); + } + + bret = dbus_message_iter_close_container(&iter, &array_iter); + assert_true(bret); + + ret = sss_sifp_parse_attr_list(ctx, reply, &attrs); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + + for (i = 0; data[i].name != NULL; i++) { + assert_non_null(attrs[i]); + assert_int_equal(attrs[i]->num_values, 1); + assert_int_equal(attrs[i]->type, SSS_SIFP_ATTR_TYPE_UINT32); + assert_string_equal(attrs[i]->name, data[i].name); + + ret = sss_sifp_find_attr_as_uint32(attrs, data[i].name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(data[i].value, out); + } + + assert_null(attrs[i]); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_parse_attr_list_empty(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + DBusMessageIter iter; + DBusMessageIter array_iter; + dbus_bool_t bret; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + + /* prepare message */ + dbus_message_iter_init_append(reply, &iter); + + bret = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &array_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&iter, &array_iter); + assert_true(bret); + + ret = sss_sifp_parse_attr_list(ctx, reply, &attrs); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_null(attrs[0]); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_fetch_attr(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + const char *name = "test-attr"; + uint32_t in = UINT32_MAX; + uint32_t out; + + /* prepare message */ + reply_variant_basic(reply, DBUS_TYPE_UINT32_AS_STRING, &in); + will_return(__wrap_dbus_connection_send_with_reply_and_block, reply); + + /* test */ + ret = sss_sifp_fetch_attr(ctx, "/test/object", "test.com", name, &attrs); + + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + assert_non_null(attrs[0]); + assert_null(attrs[1]); + + assert_int_equal(attrs[0]->num_values, 1); + assert_int_equal(attrs[0]->type, SSS_SIFP_ATTR_TYPE_UINT32); + assert_string_equal(attrs[0]->name, name); + + ret = sss_sifp_find_attr_as_uint32(attrs, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(in, out); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_fetch_all_attrs(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + DBusMessageIter iter; + DBusMessageIter array_iter; + DBusMessageIter dict_iter; + DBusMessageIter var_iter; + dbus_bool_t bret; + sss_sifp_error ret; + sss_sifp_attr **attrs = NULL; + struct { + const char *name; + uint32_t value; + } data[] = {{"attr1", 1}, {"attr2", 2}, {"attr3", 3}, {NULL, 0}}; + uint32_t out; + int i; + + /* prepare message */ + dbus_message_iter_init_append(reply, &iter); + + bret = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &array_iter); + assert_true(bret); + + for (i = 0; data[i].name != NULL; i++) { + bret = dbus_message_iter_open_container(&array_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &dict_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, + &data[i].name); + assert_true(bret); + + bret = dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_UINT32_AS_STRING, + &var_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_UINT32, + &data[i].value); + assert_true(bret); + + bret = dbus_message_iter_close_container(&dict_iter, &var_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&array_iter, &dict_iter); + assert_true(bret); + } + + bret = dbus_message_iter_close_container(&iter, &array_iter); + assert_true(bret); + will_return(__wrap_dbus_connection_send_with_reply_and_block, reply); + + ret = sss_sifp_fetch_all_attrs(ctx, "/test/object", "test.com", &attrs); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(attrs); + + for (i = 0; data[i].name != NULL; i++) { + assert_non_null(attrs[i]); + assert_int_equal(attrs[i]->num_values, 1); + assert_int_equal(attrs[i]->type, SSS_SIFP_ATTR_TYPE_UINT32); + assert_string_equal(attrs[i]->name, data[i].name); + + ret = sss_sifp_find_attr_as_uint32(attrs, data[i].name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_int_equal(data[i].value, out); + } + + assert_null(attrs[i]); + + sss_sifp_free_attrs(ctx, &attrs); + assert_null(attrs); +} + +void test_sss_sifp_fetch_object(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + DBusMessageIter iter; + DBusMessageIter array_iter; + DBusMessageIter dict_iter; + DBusMessageIter var_iter; + const char *path = "/test/object"; + const char *iface = "test.com"; + dbus_bool_t bret; + sss_sifp_error ret; + sss_sifp_object *object = NULL; + struct { + const char *name; + const char *value; + } data[] = {{"name", "test-object"}, {"a1", "a"}, {"a2", "b"}, {NULL, 0}}; + const char *out; + int i; + + /* prepare message */ + dbus_message_iter_init_append(reply, &iter); + + bret = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &array_iter); + assert_true(bret); + + for (i = 0; data[i].name != NULL; i++) { + bret = dbus_message_iter_open_container(&array_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &dict_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, + &data[i].name); + assert_true(bret); + + bret = dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_STRING_AS_STRING, + &var_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_STRING, + &data[i].value); + assert_true(bret); + + bret = dbus_message_iter_close_container(&dict_iter, &var_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&array_iter, &dict_iter); + assert_true(bret); + } + + bret = dbus_message_iter_close_container(&iter, &array_iter); + assert_true(bret); + will_return(__wrap_dbus_connection_send_with_reply_and_block, reply); + + ret = sss_sifp_fetch_object(ctx, path, iface, &object); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(object); + assert_non_null(object->attrs); + assert_non_null(object->name); + assert_non_null(object->object_path); + assert_non_null(object->interface); + + assert_string_equal(object->name, "test-object"); + assert_string_equal(object->object_path, path); + assert_string_equal(object->interface, iface); + + for (i = 0; data[i].name != NULL; i++) { + assert_non_null(object->attrs[i]); + assert_int_equal(object->attrs[i]->num_values, 1); + assert_int_equal(object->attrs[i]->type, SSS_SIFP_ATTR_TYPE_STRING); + assert_string_equal(object->attrs[i]->name, data[i].name); + + ret = sss_sifp_find_attr_as_string(object->attrs, data[i].name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_string_equal(data[i].value, out); + } + + assert_null(object->attrs[i]); + + sss_sifp_free_object(ctx, &object); + assert_null(object); +} + +void test_sss_sifp_invoke_list_zeroargs(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + dbus_bool_t bret; + sss_sifp_error ret; + char **path_out = NULL; + const char *path_in[] = {"/object/path1", "/object/path2"}; + const char **paths = path_in; + int path_in_len = 2; + int i; + + /* prepare message */ + bret = dbus_message_append_args(reply, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH, + &paths, path_in_len, + DBUS_TYPE_INVALID); + assert_true(bret); + will_return(__wrap_dbus_connection_send_with_reply_and_block, reply); + + /* test */ + ret = sss_sifp_invoke_list_ex(ctx, SSS_SIFP_PATH, SSS_SIFP_IFACE, + "MyMethod", &path_out, DBUS_TYPE_INVALID); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(path_out); + + for (i = 0; path_out[i] != NULL; i++) { + assert_true(i < path_in_len); + assert_non_null(path_out[i]); + assert_string_equal(path_in[i], path_out[i]); + } + + sss_sifp_free_string_array(ctx, &path_out); + assert_null(path_out); +} + +void test_sss_sifp_invoke_list_withargs(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + dbus_bool_t bret; + sss_sifp_error ret; + char **path_out = NULL; + const char *path_in[] = {"/object/path1", "/object/path2"}; + const char **paths = path_in; + const char *arg = "first-arg"; + int path_in_len = 2; + int i; + + /* prepare message */ + bret = dbus_message_append_args(reply, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH, + &paths, path_in_len, + DBUS_TYPE_INVALID); + assert_true(bret); + will_return(__wrap_dbus_connection_send_with_reply_and_block, reply); + + /* test */ + ret = sss_sifp_invoke_list_ex(ctx, SSS_SIFP_PATH, SSS_SIFP_IFACE, + "MyMethod", &path_out, + DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(path_out); + + for (i = 0; path_out[i] != NULL; i++) { + assert_true(i < path_in_len); + assert_non_null(path_out[i]); + assert_string_equal(path_in[i], path_out[i]); + } + + sss_sifp_free_string_array(ctx, &path_out); + assert_null(path_out); +} + +void test_sss_sifp_invoke_find_zeroargs(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + dbus_bool_t bret; + sss_sifp_error ret; + const char *path_in = "/object/path"; + char *path_out = NULL; + + /* prepare message */ + bret = dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path_in, + DBUS_TYPE_INVALID); + assert_true(bret); + will_return(__wrap_dbus_connection_send_with_reply_and_block, reply); + + /* test */ + ret = sss_sifp_invoke_find_ex(ctx, SSS_SIFP_PATH, SSS_SIFP_IFACE, + "MyMethod", &path_out, DBUS_TYPE_INVALID); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(path_out); + assert_string_equal(path_in, path_out); + + sss_sifp_free_string(ctx, &path_out); + assert_null(path_out); +} + +void test_sss_sifp_invoke_find_withargs(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *reply = test_ctx.reply; + dbus_bool_t bret; + sss_sifp_error ret; + const char *path_in = "/object/path"; + char *path_out = NULL; + const char *arg = "first-arg"; + + /* prepare message */ + bret = dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path_in, + DBUS_TYPE_INVALID); + assert_true(bret); + will_return(__wrap_dbus_connection_send_with_reply_and_block, reply); + + /* test */ + ret = sss_sifp_invoke_find_ex(ctx, SSS_SIFP_PATH, SSS_SIFP_IFACE, + "MyMethod", &path_out, + DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(path_out); + assert_string_equal(path_in, path_out); + + sss_sifp_free_string(ctx, &path_out); + assert_null(path_out); +} + +void test_sss_sifp_list_domains(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *msg_paths = NULL; + DBusMessage *msg_ldap = NULL; + DBusMessage *msg_ipa = NULL; + dbus_bool_t bret; + sss_sifp_error ret; + const char *in[] = {SSS_SIFP_PATH "/Domains/LDAP", + SSS_SIFP_PATH "/Domains/IPA"}; + const char **paths = in; + const char *names[] = {"LDAP", "IPA"}; + char **out = NULL; + int in_len = 2; + int i; + + msg_paths = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + assert_non_null(msg_paths); + + msg_ldap = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + assert_non_null(msg_ldap); + + msg_ipa = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + assert_non_null(msg_ipa); + + /* prepare message */ + bret = dbus_message_append_args(msg_paths, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH, + &paths, in_len, + DBUS_TYPE_INVALID); + assert_true(bret); + + reply_variant_basic(msg_ldap, DBUS_TYPE_STRING_AS_STRING, &names[0]); + reply_variant_basic(msg_ipa, DBUS_TYPE_STRING_AS_STRING, &names[1]); + + will_return(__wrap_dbus_connection_send_with_reply_and_block, msg_paths); + will_return(__wrap_dbus_connection_send_with_reply_and_block, msg_ldap); + will_return(__wrap_dbus_connection_send_with_reply_and_block, msg_ipa); + + /* test */ + ret = sss_sifp_list_domains(ctx, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(out); + + for (i = 0; i < in_len; i++) { + assert_non_null(out[i]); + assert_string_equal(out[i], names[i]); + } + + assert_null(out[i]); + + sss_sifp_free_string_array(ctx, &out); + assert_null(out); + + /* messages are unreferenced in the library */ +} + +void test_sss_sifp_fetch_domain_by_name(void **state) +{ + sss_sifp_ctx *ctx = test_ctx.dbus_ctx; + DBusMessage *msg_path = NULL; + DBusMessage *msg_props = NULL; + DBusMessageIter iter; + DBusMessageIter array_iter; + DBusMessageIter dict_iter; + DBusMessageIter var_iter; + dbus_bool_t bret; + sss_sifp_error ret; + const char *in =SSS_SIFP_PATH "/Domains/LDAP"; + const char *name = "LDAP"; + const char *prop = NULL; + sss_sifp_object *out = NULL; + struct { + const char *name; + const char *value; + } props[] = {{"name", name}, {"a1", "a"}, {"a2", "b"}, {NULL, 0}}; + int i; + + + msg_path = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + assert_non_null(msg_path); + + msg_props = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + assert_non_null(msg_props); + + /* prepare message */ + bret = dbus_message_append_args(msg_path, DBUS_TYPE_OBJECT_PATH, &in, + DBUS_TYPE_INVALID); + assert_true(bret); + + dbus_message_iter_init_append(msg_props, &iter); + + bret = dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &array_iter); + assert_true(bret); + + for (i = 0; props[i].name != NULL; i++) { + bret = dbus_message_iter_open_container(&array_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &dict_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, + &props[i].name); + assert_true(bret); + + bret = dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_STRING_AS_STRING, + &var_iter); + assert_true(bret); + + bret = dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_STRING, + &props[i].value); + assert_true(bret); + + bret = dbus_message_iter_close_container(&dict_iter, &var_iter); + assert_true(bret); + + bret = dbus_message_iter_close_container(&array_iter, &dict_iter); + assert_true(bret); + } + + bret = dbus_message_iter_close_container(&iter, &array_iter); + assert_true(bret); + + will_return(__wrap_dbus_connection_send_with_reply_and_block, msg_path); + will_return(__wrap_dbus_connection_send_with_reply_and_block, msg_props); + + /* test */ + ret = sss_sifp_fetch_domain_by_name(ctx, name, &out); + assert_int_equal(ret, SSS_SIFP_OK); + assert_non_null(out); + assert_non_null(out->attrs); + assert_non_null(out->name); + assert_non_null(out->object_path); + assert_non_null(out->interface); + + assert_string_equal(out->name, name); + assert_string_equal(out->object_path, in); + assert_string_equal(out->interface, "org.freedesktop.sssd.infopipe.Domains"); + + for (i = 0; props[i].name != NULL; i++) { + assert_non_null(out->attrs[i]); + assert_int_equal(out->attrs[i]->num_values, 1); + assert_int_equal(out->attrs[i]->type, SSS_SIFP_ATTR_TYPE_STRING); + assert_string_equal(out->attrs[i]->name, props[i].name); + + ret = sss_sifp_find_attr_as_string(out->attrs, props[i].name, &prop); + assert_int_equal(ret, SSS_SIFP_OK); + assert_string_equal(props[i].value, prop); + } + + assert_null(out->attrs[i]); + + sss_sifp_free_object(ctx, &out); + assert_null(out); + + /* messages are unreferenced in the library */ +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sss_sifp_strdup_valid, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_strdup_null, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_strcat_valid, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_strcat_left_null, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_strcat_right_null, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_strcat_both_null, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_object_path_valid, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_object_path_invalid, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_object_path_list_valid, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_object_path_list_invalid, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_bool, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_int16, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_uint16, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_int32, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_uint32, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_int64, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_uint64, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_string, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_object_path, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_string_dict, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_bool_array, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_bool_array_empty, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_int32_array, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_int32_array_empty, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_uint32_array, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_uint32_array_empty, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_int64_array, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_int64_array_empty, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_uint64_array, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_uint64_array_empty, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_string_array, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_string_array_empty, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_object_path_array, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_object_path_array_empty, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_string_dict_array, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_list, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_parse_attr_list_empty, + test_setup, test_teardown_parser), + cmocka_unit_test_setup_teardown(test_sss_sifp_fetch_attr, + test_setup, test_teardown_api), + cmocka_unit_test_setup_teardown(test_sss_sifp_fetch_all_attrs, + test_setup, test_teardown_api), + cmocka_unit_test_setup_teardown(test_sss_sifp_fetch_object, + test_setup, test_teardown_api), + cmocka_unit_test_setup_teardown(test_sss_sifp_invoke_list_zeroargs, + test_setup, test_teardown_api), + cmocka_unit_test_setup_teardown(test_sss_sifp_invoke_list_withargs, + test_setup, test_teardown_api), + cmocka_unit_test_setup_teardown(test_sss_sifp_invoke_find_zeroargs, + test_setup, test_teardown_api), + cmocka_unit_test_setup_teardown(test_sss_sifp_invoke_find_withargs, + test_setup, test_teardown_api), + cmocka_unit_test_setup_teardown(test_sss_sifp_list_domains, + test_setup, test_teardown_api), + cmocka_unit_test_setup_teardown(test_sss_sifp_fetch_domain_by_name, + test_setup, test_teardown_api), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/cmocka/test_sss_ssh.c b/src/tests/cmocka/test_sss_ssh.c new file mode 100644 index 0000000..10e1af7 --- /dev/null +++ b/src/tests/cmocka/test_sss_ssh.c @@ -0,0 +1,100 @@ +/* + Authors: + Pavel Reichl <preichl@redhat.com> + + Copyright (C) 2014 Red Hat + + Test for the NSS Responder ID-SID mapping interface + + 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 "util/sss_ssh.h" +#include "tests/cmocka/common_mock.h" +#include "test_utils.h" + +uint8_t key_data_noLF[] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfymad64oZkWa6q3xLXmCt/LfCRnd6yZSDp7UK6Irx5/Dv69dEKK2kBGL9Wfn+3ZDa6ov2XZrBmUthh8KOJvTw72+axox3kcJ5HwOYZCMeKbcr10RNScGuHErA1HhjTY6M9L8d0atVH2QIxw7ZHoVVnTHC4U4+541YfJkNUiOUIj65cFFZm9ULp32ZPrK+j2wW+XZkHhrZeFMlg4x4fe5FocO6ik1eqLxBejo7tMy+1m3R2a795AIguf6vNWeE5aNMd4pcmPcZHb3JOq3ItzE/3lepXD/3wqMt36EqNykBVE7aJj+LVkcEgjP9CDDsg9j9NB+AuWYmIYqrHW/Rg/vJ developer@sssd.dev.work"; + +uint8_t key_data_LF[] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfymad64oZkWa6q3xLXmCt/LfCRnd6yZSDp7UK6Irx5/Dv69dEKK2kBGL9Wfn+3ZDa6ov2XZrBmUthh8KOJvTw72+axox3kcJ5HwOYZCMeKbcr10RNScGuHErA1HhjTY6M9L8d0atVH2QIxw7ZHoVVnTHC4U4+541YfJkNUiOUIj65cFFZm9ULp32ZPrK+j2wW+XZkHhrZeFMlg4x4fe5FocO6ik1eqLxBejo7tMy+1m3R2a795AIguf6vNWeE5aNMd4pcmPcZHb3JOq3ItzE/3lepXD/3wqMt36EqNykBVE7aJj+LVkcEgjP9CDDsg9j9NB+AuWYmIYqrHW/Rg/vJ developer@sssd.dev.work\n"; + +uint8_t key_data_LFLF[] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfymad64oZkWa6q3xLXmCt/LfCRnd6yZSDp7UK6Irx5/Dv69dEKK2kBGL9Wfn+3ZDa6ov2XZrBmUthh8KOJvTw72+axox3kcJ5HwOYZCMeKbcr10RNScGuHErA1HhjTY6M9L8d0atVH2QIxw7ZHoVVnTHC4U4+541YfJkNUiOUIj65cFFZm9ULp32ZPrK+j2wW+XZkHhrZeFMlg4x4fe5FocO6ik1eqLxBejo7tMy+1m3R2a795AIguf6vNWeE5aNMd4pcmPcZHb3JOq3ItzE/3lepXD/3wqMt36EqNykBVE7aJj+LVkcEgjP9CDDsg9j9NB+AuWYmIYqrHW/Rg/vJ developer@sssd.dev.work\n\n"; + +uint8_t key_data_CRLF[] = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfymad64oZkWa6q3xLXmCt/LfCRnd6yZSDp7UK6Irx5/Dv69dEKK2kBGL9Wfn+3ZDa6ov2XZrBmUthh8KOJvTw72+axox3kcJ5HwOYZCMeKbcr10RNScGuHErA1HhjTY6M9L8d0atVH2QIxw7ZHoVVnTHC4U4+541YfJkNUiOUIj65cFFZm9ULp32ZPrK+j2wW+XZkHhrZeFMlg4x4fe5FocO6ik1eqLxBejo7tMy+1m3R2a795AIguf6vNWeE5aNMd4pcmPcZHb3JOq3ItzE/3lepXD/3wqMt36EqNykBVE7aJj+LVkcEgjP9CDDsg9j9NB+AuWYmIYqrHW/Rg/vJ developer@sssd.dev.work\r\n"; + +uint8_t key_data_CR_somewhere[] = "ssh-rsa AA\rAAB3NzaC1yc2EAAAADAQABAAABAQDfymad64oZkWa6q3xLXmCt/LfCRnd6yZSDp7UK6Irx5/Dv69dEKK2kBGL9Wf+3ZDa6ov2XZrBmUthh8KOJvTw72+axox3kcJ5HwOYZCMeKbcr10RNScGuHErA1HhjTY6M9L8d0atVH2QIxw7ZHoVVnTHC4U4+541YfJkNUiOUIj65cFFZm9ULp32ZPrK+j2wW+XZkHhrZeFMlg4x4fe5FocO6ik1eqLxBejo7tMy+1m3R2a795AIguf6vNWeE5aNMd4pcmPcZHb3JOq3ItzE/3lepXD/3wqMt36EqNykBVE7aJj+LVkcEgjP9CDDsg9j9NB+AuWYmIYqrHW/Rg/vJ developer@sssd.dev.work\n"; + +void test_textual_public_key(void **state) +{ + TALLOC_CTX *mem_ctx; + errno_t ret; + char *res; + + struct sss_ssh_pubkey pkey_null_terminated = { + .data = key_data_noLF, + .data_len = sizeof(key_data_noLF) + }; + + struct sss_ssh_pubkey pkey = { + .data = key_data_noLF, + .data_len = sizeof(key_data_noLF) - 1 /* ignore trailling '\0' */ + }; + + struct sss_ssh_pubkey pkey_LF = { + .data = key_data_LF, + .data_len = sizeof(key_data_LF) - 1 /* ignore trailling '\0' */ + }; + + struct sss_ssh_pubkey pkey_LFLF = { + .data = key_data_LFLF, + .data_len = sizeof(key_data_LFLF) - 1 /* ignore trailling '\0' */ + }; + + struct sss_ssh_pubkey pkey_CRLF = { + .data = key_data_CRLF, + .data_len = sizeof(key_data_CRLF) - 1 /* ignore trailling '\0' */ + }; + + struct sss_ssh_pubkey pkey_CR_somewhere = { + .data = key_data_CR_somewhere, + .data_len = sizeof(key_data_CR_somewhere) - 1 /* ignore traill. '\0' */ + }; + + mem_ctx = talloc_new(NULL); + assert_non_null(mem_ctx); + check_leaks_push(mem_ctx); + + ret = sss_ssh_format_pubkey(mem_ctx, &pkey, &res); + assert_int_equal(ret, EOK); + talloc_free(res); + + ret = sss_ssh_format_pubkey(mem_ctx, &pkey_LF, &res); + assert_int_equal(ret, EOK); + talloc_free(res); + + ret = sss_ssh_format_pubkey(mem_ctx, &pkey_LFLF, &res); + assert_int_equal(ret, EINVAL); + + ret = sss_ssh_format_pubkey(mem_ctx, &pkey_null_terminated, &res); + assert_int_equal(ret, EINVAL); + + ret = sss_ssh_format_pubkey(mem_ctx, &pkey_CRLF, &res); + assert_int_equal(ret, EINVAL); + + ret = sss_ssh_format_pubkey(mem_ctx, &pkey_CR_somewhere, &res); + assert_int_equal(ret, EINVAL); + + assert_true(check_leaks_pop(mem_ctx) == true); + talloc_free(mem_ctx); +} diff --git a/src/tests/cmocka/test_sssd_krb5_localauth_plugin.c b/src/tests/cmocka/test_sssd_krb5_localauth_plugin.c new file mode 100644 index 0000000..6d66d9e --- /dev/null +++ b/src/tests/cmocka/test_sssd_krb5_localauth_plugin.c @@ -0,0 +1,227 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2017 Red Hat + + Test for the MIT Kerberos localauth plugin + + 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 <errno.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <stdbool.h> +#include <nss.h> +#include <sys/types.h> +#include <pwd.h> + +#include <krb5/krb5.h> +#include <krb5/localauth_plugin.h> + +#include "tests/cmocka/common_mock.h" + +struct _nss_sss_getpwnam_r_test_data { + uid_t uid; + const char *name; + enum nss_status status; +}; + +enum nss_status _nss_sss_getpwnam_r(const char *name, struct passwd *result, + char *buffer, size_t buflen, int *errnop) +{ + struct _nss_sss_getpwnam_r_test_data *test_data; + + assert_non_null(name); + assert_non_null(result); + assert_non_null(buffer); + assert_int_not_equal(buflen, 0); + assert_non_null(errnop); + + test_data = sss_mock_ptr_type(struct _nss_sss_getpwnam_r_test_data *); + + result->pw_uid = test_data->uid; + if (test_data->name != NULL) { + assert_true(buflen > strlen(test_data->name)); + strncpy(buffer, test_data->name, buflen); + result->pw_name = buffer; + } + + return test_data->status; +} + +int getpwnam_r(const char *name, struct passwd *pwd, + char *buffer, size_t buflen, struct passwd **result) +{ + struct _nss_sss_getpwnam_r_test_data *test_data; + + assert_non_null(name); + assert_non_null(pwd); + assert_non_null(result); + assert_non_null(buffer); + assert_int_not_equal(buflen, 0); + + test_data = sss_mock_ptr_type(struct _nss_sss_getpwnam_r_test_data *); + + if (test_data->status != NSS_STATUS_SUCCESS) { + *result = NULL; + return test_data->status == NSS_STATUS_NOTFOUND ? ENOENT : EIO; + } + + pwd->pw_uid = test_data->uid; + if (test_data->name != NULL) { + assert_true(buflen > strlen(test_data->name)); + strncpy(buffer, test_data->name, buflen); + pwd->pw_name = buffer; + } + *result = pwd; + + return 0; +} + + +krb5_error_code +localauth_sssd_initvt(krb5_context context, int maj_ver, int min_ver, + krb5_plugin_vtable vtable); + +void test_localauth_sssd_initvt(void **state) +{ + krb5_error_code kerr; + struct krb5_localauth_vtable_st vtable = { 0 }; + + kerr = localauth_sssd_initvt(NULL, 0, 0, (krb5_plugin_vtable) &vtable); + assert_int_equal(kerr, KRB5_PLUGIN_VER_NOTSUPP); + + kerr = localauth_sssd_initvt(NULL, 1, 1, (krb5_plugin_vtable) &vtable); + assert_int_equal(kerr, 0); + assert_string_equal(vtable.name, "sssd"); + assert_null(vtable.init); + assert_null(vtable.fini); + assert_non_null(vtable.an2ln); + assert_non_null(vtable.userok); + assert_non_null(vtable.free_string); +} + +void test_sss_userok(void **state) +{ + krb5_error_code kerr; + struct krb5_localauth_vtable_st vtable = { 0 }; + krb5_context krb5_ctx; + krb5_principal princ; + size_t c; + + struct test_data { + struct _nss_sss_getpwnam_r_test_data d1; + struct _nss_sss_getpwnam_r_test_data d2; + krb5_error_code kerr; + } test_data[] = { + {{ 1234, NULL, NSS_STATUS_SUCCESS}, { 1234, NULL, NSS_STATUS_SUCCESS}, + 0}, + /* second _nss_sss_getpwnam_r() is never called because the first one + * already returned an error */ + {{ 1234, NULL, NSS_STATUS_NOTFOUND}, { 0, NULL, 0}, + KRB5_PLUGIN_NO_HANDLE}, + {{ 1234, NULL, NSS_STATUS_SUCCESS}, { 1234, NULL, NSS_STATUS_NOTFOUND}, + KRB5_PLUGIN_NO_HANDLE}, + {{ 1234, NULL, NSS_STATUS_SUCCESS}, { 4321, NULL, NSS_STATUS_SUCCESS}, + KRB5_PLUGIN_NO_HANDLE}, + /* second _nss_sss_getpwnam_r() is never called because the first one + * already returned an error */ + {{ 1234, NULL, NSS_STATUS_UNAVAIL}, { 0, NULL, 0}, + KRB5_PLUGIN_NO_HANDLE}, + {{ 1234, NULL, NSS_STATUS_SUCCESS}, { 1234, NULL, NSS_STATUS_TRYAGAIN}, + KRB5_PLUGIN_NO_HANDLE}, + {{ 0, NULL, 0 }, {0 , NULL, 0}, 0} + }; + + kerr = krb5_init_context(&krb5_ctx); + assert_int_equal(kerr, 0); + + kerr = localauth_sssd_initvt(krb5_ctx, 1, 1, (krb5_plugin_vtable) &vtable); + assert_int_equal(kerr, 0); + + kerr = krb5_parse_name(krb5_ctx, "name@REALM", &princ); + assert_int_equal(kerr, 0); + + + for (c = 0; test_data[c].d1.uid != 0; c++) { + will_return(_nss_sss_getpwnam_r, &test_data[c].d1); + if (test_data[c].d2.uid != 0) { + will_return(getpwnam_r, &test_data[c].d2); + } + kerr = vtable.userok(krb5_ctx, NULL, princ, "name"); + assert_int_equal(kerr, test_data[c].kerr); + } + + krb5_free_principal(krb5_ctx, princ); + krb5_free_context(krb5_ctx); +} + +void test_sss_an2ln(void **state) +{ + krb5_error_code kerr; + struct krb5_localauth_vtable_st vtable = { 0 }; + krb5_context krb5_ctx; + krb5_principal princ; + size_t c; + char *lname; + + struct test_data { + struct _nss_sss_getpwnam_r_test_data d; + krb5_error_code kerr; + } test_data[] = { + { { 0, "my_name", NSS_STATUS_SUCCESS}, 0}, + { { 0, "my_name", NSS_STATUS_NOTFOUND}, KRB5_LNAME_NOTRANS}, + { { 0, "my_name", NSS_STATUS_UNAVAIL}, EIO}, + { { 0, NULL, 0 } , 0} + }; + + kerr = krb5_init_context(&krb5_ctx); + assert_int_equal(kerr, 0); + + kerr = localauth_sssd_initvt(krb5_ctx, 1, 1, (krb5_plugin_vtable) &vtable); + assert_int_equal(kerr, 0); + + kerr = krb5_parse_name(krb5_ctx, "name@REALM", &princ); + assert_int_equal(kerr, 0); + + + for (c = 0; test_data[c].d.name != NULL; c++) { + will_return(_nss_sss_getpwnam_r, &test_data[c].d); + kerr = vtable.an2ln(krb5_ctx, NULL, NULL, NULL, princ, &lname); + assert_int_equal(kerr, test_data[c].kerr); + if (kerr == 0) { + assert_string_equal(lname, test_data[c].d.name); + vtable.free_string(krb5_ctx, NULL, lname); + } + } + + krb5_free_principal(krb5_ctx, princ); + krb5_free_context(krb5_ctx); +} + +int main(int argc, const char *argv[]) +{ + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_localauth_sssd_initvt), + cmocka_unit_test(test_sss_userok), + cmocka_unit_test(test_sss_an2ln), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_sssd_krb5_locator_plugin.c b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c new file mode 100644 index 0000000..1b68383 --- /dev/null +++ b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c @@ -0,0 +1,787 @@ +/* + SSSD + + Unit test for SSSD's MIT Kerberos locator plugin + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2018 Red Hat + + 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 "config.h" + +#include <popt.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <string.h> +#include <fcntl.h> +#include <netdb.h> +#include <krb5/krb5.h> +#include <krb5/locate_plugin.h> + +#include "tests/cmocka/common_mock.h" + +#define TEST_REALM "TEST.REALM" +#define TEST_IP_1 "123.231.132.213" +#define TEST_IPV6_1_PURE "7025:4d2d:2b06:e321:d971:16c0:6eeb:cc41" +#define TEST_IPV6_1 "["TEST_IPV6_1_PURE"]" +#define TEST_SERVICE_1 "22334" +#define TEST_SERVICE_2 "54321" +#define TEST_IP_1_WITH_SERVICE TEST_IP_1":"TEST_SERVICE_1 +#define TEST_IPV6_1_WITH_SERVICE TEST_IPV6_1":"TEST_SERVICE_2 + +#define TEST_IP_1_WITH_SERVICE_2 TEST_IP_1":"TEST_SERVICE_2 +#define TEST_IPV6_1_WITH_SERVICE_1 TEST_IPV6_1":"TEST_SERVICE_1 + +struct test_state { + void *dummy; +}; + +static int setup(void **state) +{ + struct test_state *ts = NULL; + + assert_true(leak_check_setup()); + + ts = talloc(global_talloc_context, struct test_state); + assert_non_null(ts); + + check_leaks_push(ts); + *state = (void *)ts; + + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + unlink(TEST_PUBCONF_PATH"/kpasswdinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + + return 0; +} + +static int teardown(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + + assert_non_null(ts); + + assert_true(check_leaks_pop(ts)); + talloc_free(ts); + assert_true(leak_check_teardown()); + return 0; +} + +/* Taken from MIT Kerberos src/lib/krb5/os/locate_kdc.c and + * lib/krb5/os/os-proto.h */ + +typedef enum { + TCP_OR_UDP = 0, + TCP, + UDP, + HTTPS, +} k5_transport; + +/* A single server hostname or address. */ +struct server_entry { + char *hostname; /* NULL -> use addrlen/addr instead */ + int port; /* Used only if hostname set */ + k5_transport transport; /* May be 0 for UDP/TCP if hostname set */ + char *uri_path; /* Used only if transport is HTTPS */ + int family; /* May be 0 (aka AF_UNSPEC) if hostname set */ + int master; /* True, false, or -1 for unknown. */ + size_t addrlen; + struct sockaddr_storage addr; +}; + +/* A list of server hostnames/addresses. */ +struct serverlist { + struct server_entry *servers; + size_t nservers; +}; +#define SERVERLIST_INIT { NULL, 0 } + +/* Free up everything pointed to by the serverlist structure, but don't + * * free the structure itself. */ +void +k5_free_serverlist (struct serverlist *list) +{ + size_t i; + + for (i = 0; i < list->nservers; i++) { + free(list->servers[i].hostname); + free(list->servers[i].uri_path); + } + free(list->servers); + list->servers = NULL; + list->nservers = 0; +} + +/* Make room for a new server entry in list and return a pointer to the new + * entry. (Do not increment list->nservers.) */ +static struct server_entry * +new_server_entry(struct serverlist *list) +{ + struct server_entry *newservers, *entry; + size_t newspace = (list->nservers + 1) * sizeof(struct server_entry); + + newservers = realloc(list->servers, newspace); + if (newservers == NULL) + return NULL; + list->servers = newservers; + entry = &newservers[list->nservers]; + memset(entry, 0, sizeof(*entry)); + entry->master = -1; + return entry; +} + +/* Add an address entry to list. */ +static int +add_addr_to_list(struct serverlist *list, k5_transport transport, int family, + size_t addrlen, struct sockaddr *addr) +{ + struct server_entry *entry; + + entry = new_server_entry(list); + if (entry == NULL) + return ENOMEM; + entry->transport = transport; + entry->family = family; + entry->hostname = NULL; + entry->uri_path = NULL; + entry->addrlen = addrlen; + memcpy(&entry->addr, addr, addrlen); + list->nservers++; + return 0; +} + +struct module_callback_data { + int out_of_mem; + struct serverlist *list; +}; + +static int +module_callback(void *cbdata, int socktype, struct sockaddr *sa) +{ + struct module_callback_data *d = cbdata; + size_t addrlen; + k5_transport transport; + + if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) + return 0; + if (sa->sa_family == AF_INET) + addrlen = sizeof(struct sockaddr_in); + else if (sa->sa_family == AF_INET6) + addrlen = sizeof(struct sockaddr_in6); + else + return 0; + transport = (socktype == SOCK_STREAM) ? TCP : UDP; + if (add_addr_to_list(d->list, transport, sa->sa_family, addrlen, + sa) != 0) { + /* Assumes only error is ENOMEM. */ + d->out_of_mem = 1; + return 1; + } + return 0; +} + +krb5_error_code sssd_krb5_locator_init(krb5_context context, + void **private_data); +void sssd_krb5_locator_close(void *private_data); + +krb5_error_code sssd_krb5_locator_lookup(void *private_data, + enum locate_service_type svc, + const char *realm, + int socktype, + int family, + int (*cbfunc)(void *, int, struct sockaddr *), + void *cbdata); + +void test_init(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_failed_lookup(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + struct module_callback_data cbdata = { 0 }; + + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_lookup(NULL, -1, NULL, -1, -1, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, -1, NULL, -1, -1, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , NULL, -1, -1, + NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, -1, + -1, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, -1, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_empty(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct module_callback_data cbdata = { 0 }; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT, 0777); + assert_int_not_equal(fd, -1); + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_single(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct serverlist list = SERVERLIST_INIT; + struct module_callback_data cbdata = { 0 }; + ssize_t s; + int ret; + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + + cbdata.list = &list; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + s = write(fd, TEST_IP_1, sizeof(TEST_IP_1)); + assert_int_equal(s, sizeof(TEST_IP_1)); + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + /* We asked for AF_INET6, but TEST_IP_1 is IPv4 */ + assert_int_equal(list.nservers, 0); + assert_null(list.servers); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + assert_int_equal(list.servers[0].addrlen, 16); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_UNSPEC, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + assert_int_equal(list.servers[0].addrlen, 16); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +struct test_data { + const char *ip; + bool found; +}; + +void test_multi_check_results(struct test_data *test_data, + struct serverlist *list, + const char *exp_service) +{ + int ret; + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + size_t c; + size_t d; + + /* To make sure each result from list has a matching entry in test_data we + * use a flag to mark found entries, this way we can properly detect is + * the same address is used multiple times. */ + for (d = 0; test_data[d].ip != NULL; d++) { + test_data[d].found = false; + } + + for (c = 0; c < list->nservers; c++) { + ret = getnameinfo((struct sockaddr *) &list->servers[c].addr, + list->servers[c].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(exp_service, service); + for (d = 0; test_data[d].ip != NULL; d++) { + /* Compare result with test_data, be aware that the test_data has + * '[]' around IPv& addresses */ + if (strncmp(host, + test_data[d].ip + (test_data[d].ip[0] == '[' ? 1 : 0), + strlen(host)) == 0 && !test_data[d].found) { + test_data[d].found = true; + break; + } + } + /* Make sure we found the result in the list */ + assert_non_null(test_data[d].ip); + } +} + +void test_multi(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct serverlist list = SERVERLIST_INIT; + struct module_callback_data cbdata = { 0 }; + ssize_t s; + size_t c; + struct test_data test_data[] = { + {TEST_IP_1, false}, + {TEST_IPV6_1, false}, + {"[c89a:565b:4510:5b9f:41fe:ea81:87a0:f21b]", false}, + {"155.42.66.53", false}, + {"[f812:5941:ba69:2bae:e806:3b68:770d:d75e]", false}, + {"[3ad3:9dda:50e4:3c82:548f:eaa1:e120:6dd]", false}, + {"55.116.79.183", false}, + {"[ce8a:ee99:98cd:d8cd:218d:393e:d5a9:dc52]", false}, + /* the following address is added twice to check if + * an address can be added more than once. */ + {"37.230.88.162", false}, + {"37.230.88.162", false}, + {NULL, false} }; + + cbdata.list = &list; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + for (c = 0; test_data[c].ip != NULL; c++) { + s = write(fd, test_data[c].ip, strlen(test_data[c].ip)); + assert_int_equal(s, strlen(test_data[c].ip)); + s = write(fd, "\n", 1); + assert_int_equal(s, 1); + } + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 5); + assert_non_null(list.servers); + test_multi_check_results(test_data, &list, "88"); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 5); + assert_non_null(list.servers); + test_multi_check_results(test_data, &list, "88"); + + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_UNSPEC, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 10); + assert_non_null(list.servers); + test_multi_check_results(test_data, &list, "88"); + + k5_free_serverlist(&list); + + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_service(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct serverlist list = SERVERLIST_INIT; + struct module_callback_data cbdata = { 0 }; + ssize_t s; + int ret; + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + + cbdata.list = &list; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + s = write(fd, TEST_IP_1_WITH_SERVICE, sizeof(TEST_IP_1_WITH_SERVICE)); + assert_int_equal(s, sizeof(TEST_IP_1_WITH_SERVICE)); + s = write(fd, "\n", 1); + assert_int_equal(s, 1); + s = write(fd, TEST_IPV6_1_WITH_SERVICE, sizeof(TEST_IPV6_1_WITH_SERVICE)); + assert_int_equal(s, sizeof(TEST_IPV6_1_WITH_SERVICE)); + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IPV6_1_PURE, host); + assert_string_equal(TEST_SERVICE_2, service); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal(TEST_SERVICE_1, service); + + k5_free_serverlist(&list); + + /* locate_service_master_kdc should get the default port 88 if kpasswdinfo + * does not exists. */ + kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IPV6_1_PURE, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_kpasswd_and_master_kdc(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct serverlist list = SERVERLIST_INIT; + struct module_callback_data cbdata = { 0 }; + ssize_t s; + int ret; + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + + cbdata.list = &list; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + s = write(fd, TEST_IP_1_WITH_SERVICE, sizeof(TEST_IP_1_WITH_SERVICE)); + assert_int_equal(s, sizeof(TEST_IP_1_WITH_SERVICE)); + s = write(fd, "\n", 1); + assert_int_equal(s, 1); + s = write(fd, TEST_IPV6_1_WITH_SERVICE, sizeof(TEST_IPV6_1_WITH_SERVICE)); + assert_int_equal(s, sizeof(TEST_IPV6_1_WITH_SERVICE)); + close(fd); + fd = open(TEST_PUBCONF_PATH"/kpasswdinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + s = write(fd, TEST_IP_1_WITH_SERVICE_2, sizeof(TEST_IP_1_WITH_SERVICE_2)); + assert_int_equal(s, sizeof(TEST_IP_1_WITH_SERVICE_2)); + s = write(fd, "\n", 1); + assert_int_equal(s, 1); + s = write(fd, TEST_IPV6_1_WITH_SERVICE_1, + sizeof(TEST_IPV6_1_WITH_SERVICE_1)); + assert_int_equal(s, sizeof(TEST_IPV6_1_WITH_SERVICE_1)); + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kpasswd, TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal(TEST_SERVICE_2, service); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kpasswd , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IPV6_1_PURE, host); + assert_string_equal(TEST_SERVICE_1, service); + + k5_free_serverlist(&list); + + /* locate_service_master_kdc should use the default KDC port 88 and not + * the one set in the kpasswdinfo file. */ + kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IPV6_1_PURE, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + unlink(TEST_PUBCONF_PATH"/kpasswdinfo."TEST_REALM); + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int ret; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_init, + setup, teardown), + cmocka_unit_test_setup_teardown(test_failed_lookup, + setup, teardown), + cmocka_unit_test_setup_teardown(test_empty, + setup, teardown), + cmocka_unit_test_setup_teardown(test_single, + setup, teardown), + cmocka_unit_test_setup_teardown(test_multi, + setup, teardown), + cmocka_unit_test_setup_teardown(test_service, + setup, teardown), + cmocka_unit_test_setup_teardown(test_kpasswd_and_master_kdc, + setup, teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + ret = cmocka_run_group_tests(tests, NULL, NULL); + + return ret; +} diff --git a/src/tests/cmocka/test_string_utils.c b/src/tests/cmocka/test_string_utils.c new file mode 100644 index 0000000..f11fc21 --- /dev/null +++ b/src/tests/cmocka/test_string_utils.c @@ -0,0 +1,372 @@ +/* + Authors: + Lukas Slebodnik <slebodnikl@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 "tests/cmocka/common_mock.h" + +void test_replace_whitespaces(void **state) +{ + TALLOC_CTX *mem_ctx; + const char *input_str = "Lorem ipsum dolor sit amet"; + const char *res; + size_t i; + + struct { + const char *input; + const char *output; + const char replace_char; + } data_set[] = { + { "", "", '-' }, + { " ", "-", '-' }, + { "abcd", "abcd", '-' }, + { "a b c d", "a-b-c-d", '-' }, + { " a b c d ", "-a-b-c-d-", '-' }, + { " ", "^", '^' }, + { "abcd", "abcd", '^' }, + { "a b c d", "a^b^c^d", '^' }, + { " a b c d ", "^a^b^c^d^", '^' }, + { " ", "^", '^' }, + { " ", " ", ' ' }, + { " ", " ", ' ' }, + { "abcd", "abcd", ' ' }, + { "a b c d", "a b c d", ' ' }, + { "a b^c d", "a b^c d", '^' }, + { NULL, NULL, '\0' }, + }; + + mem_ctx = talloc_new(NULL); + assert_non_null(mem_ctx); + check_leaks_push(mem_ctx); + + res = sss_replace_space(mem_ctx, input_str, '\0'); + assert_string_equal(res, input_str); + talloc_zfree(res); + + res = sss_replace_space(mem_ctx, input_str, '\0'); + assert_string_equal(res, input_str); + talloc_zfree(res); + + for (i=0; data_set[i].input != NULL; ++i) { + res = sss_replace_space(mem_ctx, data_set[i].input, + data_set[i].replace_char); + assert_non_null(res); + assert_string_equal(res, data_set[i].output); + talloc_zfree(res); + } + + assert_true(check_leaks_pop(mem_ctx) == true); + talloc_free(mem_ctx); +} + +void test_reverse_replace_whitespaces(void **state) +{ + TALLOC_CTX *mem_ctx; + char *input_str = discard_const_p(char, "Lorem ipsum dolor sit amet"); + char *res; + size_t i; + + struct { + const char *input; + const char *output; + const char replace_char; + } data_set[] = { + { "", "", '-' }, + { "-", " ", '-' }, + { "----", " ", '-' }, + { "abcd", "abcd", '-' }, + { "a-b-c-d", "a b c d", '-' }, + { "-a-b-c-d-", " a b c d ", '-' }, + { "a b c d", "a b c d", '-' }, + { " a b c d ", " a b c d ", '-' }, + { "^", " ", '^' }, + { "^^^^", " ", '^' }, + { "abcd", "abcd", '^' }, + { "a^b^c^d", "a b c d", '^' }, + { "^a^b^c^d^", " a b c d ", '^' }, + { " ", " ", ' ' }, + { " ", " ", ' ' }, + { "abcd", "abcd", ' ' }, + { "a b c d", "a b c d", ' ' }, + { " a b c d ", " a b c d ", ' ' }, + { "a b^c d", "a b^c d", '^' }, + { NULL, NULL, '\0' }, + }; + + mem_ctx = talloc_new(NULL); + assert_non_null(mem_ctx); + check_leaks_push(mem_ctx); + + res = sss_reverse_replace_space(mem_ctx, input_str, '\0'); + assert_string_equal(res, input_str); + talloc_free(res); + + res = sss_reverse_replace_space(mem_ctx, input_str, '\0'); + assert_string_equal(res, input_str); + talloc_free(res); + + for (i=0; data_set[i].input != NULL; ++i) { + input_str = discard_const_p(char, data_set[i].input); + res = sss_reverse_replace_space(mem_ctx, input_str, + data_set[i].replace_char); + assert_non_null(res); + assert_string_equal(res, data_set[i].output); + talloc_zfree(res); + } + + assert_true(check_leaks_pop(mem_ctx) == true); + talloc_free(mem_ctx); +} + +void test_guid_blob_to_string_buf(void **state) +{ + int ret; + char str_buf[GUID_STR_BUF_SIZE]; + size_t c; + + /* How to get test data: + * The objectGUID attribute contains a 16byte long binary value + * representing the GUID of the object. This data can be converted + * manually to the string representation but it might be easier to use + * LDAP_SERVER_EXTENDED_DN_OID as described in [MS-ADST] section + * 3.1.1.3.4.1.5. This is an LDAP extended control which adds the GUID and + * the SID to the DN of an object. This can be activate with the -E + * ldapsearch option like: + * + * ldapsearch -E 1.2.840.113556.1.4.529=::MAMCAQE= .... + * + * where 'MAMCAQE=' is the base64 encoded BER sequence with the integer + * value 1 (see [MS-ADTS] for details about possible values). + * + * Btw, if you want to use the string representation of a GUID to search + * for an object in AD you have to use the GUID as the search base in the + * following form: + * + * ldapsearch b '<GUID=fea80d8d-dbd5-4f84-8574-7db0477f962e>' ... + * + * (please note that the '<' and '>' are really needed). + */ + struct test_data { + uint8_t blob[16]; + const char *guid_str; + } test_data[] = { + {{0x8d, 0x0d, 0xa8, 0xfe, 0xd5, 0xdb, 0x84, 0x4f, + 0x85, 0x74, 0x7d, 0xb0, 0x47, 0x7f, 0x96, 0x2e}, + "fea80d8d-dbd5-4f84-8574-7db0477f962e"}, + {{0x91, 0x7e, 0x2e, 0xf8, 0x4e, 0x44, 0xfa, 0x4e, + 0xb1, 0x13, 0x08, 0x98, 0x63, 0x49, 0x6c, 0xc6}, + "f82e7e91-444e-4efa-b113-089863496cc6"}, + {{0}, NULL} + }; + + ret = guid_blob_to_string_buf(NULL, str_buf, GUID_STR_BUF_SIZE); + assert_int_equal(ret, EINVAL); + + ret = guid_blob_to_string_buf((const uint8_t *) "1234567812345678", NULL, + GUID_STR_BUF_SIZE); + assert_int_equal(ret, EINVAL); + + ret = guid_blob_to_string_buf((const uint8_t *) "1234567812345678", str_buf, 0); + assert_int_equal(ret, EINVAL); + + for (c = 0; test_data[c].guid_str != NULL; c++) { + ret = guid_blob_to_string_buf(test_data[c].blob, str_buf, + sizeof(str_buf)); + assert_int_equal(ret, EOK); + assert_string_equal(test_data[c].guid_str, str_buf); + } +} + +void test_get_last_x_chars(void **state) +{ + const char *s; + + s = get_last_x_chars(NULL, 0); + assert_null(s); + + s = get_last_x_chars("abc", 0); + assert_non_null(s); + assert_string_equal(s, ""); + + s = get_last_x_chars("abc", 1); + assert_non_null(s); + assert_string_equal(s, "c"); + + s = get_last_x_chars("abc", 2); + assert_non_null(s); + assert_string_equal(s, "bc"); + + s = get_last_x_chars("abc", 3); + assert_non_null(s); + assert_string_equal(s, "abc"); + + s = get_last_x_chars("abc", 4); + assert_non_null(s); + assert_string_equal(s, "abc"); +} + +void test_concatenate_string_array(void **state) +{ + TALLOC_CTX *mem_ctx; + char **a1; + size_t a1_len = 2; + char **a2; + size_t a2_len = 3; + char **res; + size_t c; + + mem_ctx = talloc_new(NULL); + assert_non_null(mem_ctx); + check_leaks_push(mem_ctx); + + res = concatenate_string_array(mem_ctx, NULL, 0, NULL, 0); + assert_non_null(res); + assert_null(res[0]); + talloc_free(res); + + a1 = talloc_array(mem_ctx, char *, a1_len); + assert_non_null(a1); + for (c = 0; c < a1_len; c++) { + a1[c] = talloc_asprintf(a1, "%zu", c); + assert_non_null(a1[c]); + } + + a2 = talloc_array(mem_ctx, char *, a2_len); + assert_non_null(a2); + for (c = 0; c < a2_len; c++) { + a2[c] = talloc_asprintf(a2, "%zu", c + a1_len); + assert_non_null(a2[c]); + } + + res = concatenate_string_array(mem_ctx, a1, a1_len, a2, a2_len); + assert_non_null(res); + assert_null(res[a1_len + a2_len]); + for (c = 0; c < (a1_len + a2_len); c++) { + assert_string_equal(res[c], talloc_asprintf(res, "%zu", c)); + } + + talloc_free(res); + /* Since concatenate_string_array() uses talloc_realloc on a1 it should + * not be needed to free a1 explicitly. */ + talloc_free(a2); + + assert_true(check_leaks_pop(mem_ctx) == true); + talloc_free(mem_ctx); +} + +void test_mod_defaults_list(void **state) +{ + int ret; + char **list; + size_t c; + size_t t; + const char *default_list[] = {"abc", "def", "ghi", "jkl", NULL}; + + const char *mod_list_error1[] = {"abc", "+def", NULL}; + const char *mod_list_error2[] = {"-abc", "def", NULL}; + + const char *mod_list1[] = {"-abc", NULL}; + const char *res_list1[] = {"def", "ghi", "jkl", NULL}; + + const char *mod_list2[] = {"-def", NULL}; + const char *res_list2[] = {"abc", "ghi", "jkl", NULL}; + + const char *mod_list3[] = {"-jkl", NULL}; + const char *res_list3[] = {"abc", "def", "ghi", NULL}; + + const char *mod_list4[] = {"+abc", NULL}; + const char *res_list4[] = {"abc", "abc", "def", "ghi", "jkl", NULL}; + + const char *mod_list5[] = {"+xyz", NULL}; + const char *res_list5[] = {"xyz", "abc", "def", "ghi", "jkl", NULL}; + + const char *mod_list6[] = {"+xyz", "-xyz", NULL}; + const char *res_list6[] = {"abc", "def", "ghi", "jkl", NULL}; + + const char *mod_list7[] = {"-xyz", NULL}; + const char *res_list7[] = {"abc", "def", "ghi", "jkl", NULL}; + + struct test_data { + const char **mod_list; + const char **res_list; + } test_data[] = { + {mod_list1, res_list1 }, + {mod_list2, res_list2 }, + {mod_list3, res_list3 }, + {mod_list4, res_list4 }, + {mod_list5, res_list5 }, + {mod_list6, res_list6 }, + {mod_list7, res_list7 }, + {NULL, NULL} + }; + + ret = mod_defaults_list(NULL, NULL, NULL, NULL); + assert_int_equal(ret, EOK); + + list = NULL; + ret = mod_defaults_list(NULL, NULL, NULL, &list); + assert_int_equal(ret, EOK); + assert_non_null(list); + assert_null(list[0]); + talloc_free(list); + + list = NULL; + ret = mod_defaults_list(NULL, default_list, NULL, &list); + assert_int_equal(ret, EOK); + assert_non_null(list); + for (c = 0; default_list[c] != NULL; c++) { + assert_string_equal(list[c], default_list[c]); + } + assert_null(list[c]); + talloc_free(list); + + list = NULL; + ret = mod_defaults_list(NULL, default_list, discard_const(mod_list_error1), + &list); + assert_int_equal(ret, EINVAL); + assert_null(list); + + list = NULL; + ret = mod_defaults_list(NULL, default_list, discard_const(mod_list_error2), + &list); + assert_int_equal(ret, EINVAL); + assert_null(list); + + list = NULL; + ret = mod_defaults_list(NULL, NULL, discard_const(mod_list4), &list); + assert_int_equal(ret, EOK); + assert_non_null(list); + assert_string_equal(list[0], "abc"); + assert_null(list[1]); + talloc_free(list); + + for (t = 0; test_data[t].res_list != NULL; t++) { + list = NULL; + ret = mod_defaults_list(NULL, default_list, + discard_const(test_data[t].mod_list), &list); + assert_int_equal(ret, EOK); + assert_non_null(list); + for (c = 0; test_data[t].res_list[c] != NULL; c++) { + assert_non_null(list[c]); + assert_string_equal(list[c], test_data[t].res_list[c]); + } + assert_null(list[c]); + talloc_free(list); + } +} diff --git a/src/tests/cmocka/test_sysdb_certmap.c b/src/tests/cmocka/test_sysdb_certmap.c new file mode 100644 index 0000000..8e84d18 --- /dev/null +++ b/src/tests/cmocka/test_sysdb_certmap.c @@ -0,0 +1,282 @@ +/* + SSSD + + sysdb_certmap - Tests for sysdb certmap related calls + + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2017 Red Hat + + 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" + +#define TESTS_PATH "certmap_" BASE_FILE_STEM +#define TEST_CONF_DB "test_sysdb_certmap.ldb" +#define TEST_ID_PROVIDER "ldap" +#define TEST_DOM_NAME "certmap_test" + +struct certmap_test_ctx { + struct sss_test_ctx *tctx; +}; + +static int test_sysdb_setup(void **state) +{ + struct certmap_test_ctx *test_ctx; + struct sss_test_conf_param params[] = { + { NULL, NULL }, /* Sentinel */ + }; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, + struct certmap_test_ctx); + assert_non_null(test_ctx); + check_leaks_push(test_ctx); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, params); + assert_non_null(test_ctx->tctx); + + *state = test_ctx; + return 0; +} + +static int test_sysdb_teardown(void **state) +{ + struct certmap_test_ctx *test_ctx = + talloc_get_type(*state, struct certmap_test_ctx); + + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + talloc_free(test_ctx->tctx); + assert_true(check_leaks_pop(test_ctx)); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_sysdb_get_certmap_not_exists(void **state) +{ + int ret; + struct certmap_info **certmap; + bool user_name_hint; + struct certmap_test_ctx *ctctx = talloc_get_type(*state, + struct certmap_test_ctx); + + ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, + &user_name_hint); + assert_int_equal(ret, EOK); + assert_null(certmap); +} + +static void check_certmap(struct certmap_info *m, struct certmap_info *r, + size_t exp_domains) +{ + size_t d; + + assert_non_null(r); + assert_non_null(m); + assert_string_equal(m->name, r->name); + + if (r->map_rule == NULL) { + assert_null(m->map_rule); + } else { + assert_string_equal(m->map_rule, r->map_rule); + } + + if (r->match_rule == NULL) { + assert_null(m->match_rule); + } else { + assert_string_equal(m->match_rule, r->match_rule); + } + + assert_int_equal(m->priority, r->priority); + assert_non_null(m->domains); + if (r->domains == NULL) { + assert_null(m->domains[0]); + } else { + for (d = 0; r->domains[d]; d++) { + assert_non_null(m->domains[d]); + assert_true(string_in_list(m->domains[d], discard_const(r->domains), + true)); + } + + assert_int_equal(d, exp_domains); + } + +} + +static void test_sysdb_update_certmap(void **state) +{ + int ret; + const char *domains[] = { "dom1.test", "dom2.test", "dom3.test", NULL }; + struct certmap_info map_a = { discard_const("map_a"), 11, + discard_const("abc"), discard_const("def"), + NULL }; + struct certmap_info map_b = { discard_const("map_b"), UINT_MAX, + discard_const("abc"), NULL, domains }; + struct certmap_info map_c = { discard_const("cn=map_c,dc=sssd,dc=org"), + UINT_MAX, discard_const("abc"), NULL, + domains }; + + struct certmap_info *certmap_empty[] = { NULL }; + struct certmap_info *certmap_a[] = { &map_a, NULL }; + struct certmap_info *certmap_b[] = { &map_b, NULL }; + struct certmap_info *certmap_ab[] = { &map_a, &map_b, NULL }; + struct certmap_info *certmap_c[] = { &map_c, NULL }; + struct certmap_info **certmap; + struct certmap_test_ctx *ctctx = talloc_get_type(*state, + struct certmap_test_ctx); + bool user_name_hint; + + ret = sysdb_update_certmap(ctctx->tctx->sysdb, NULL, false); + assert_int_equal(ret, EINVAL); + + ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_empty, false); + assert_int_equal(ret, EOK); + + ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, + &user_name_hint); + assert_int_equal(ret, EOK); + assert_null(certmap); + + ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_a, false); + assert_int_equal(ret, EOK); + + ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, + &user_name_hint); + assert_int_equal(ret, EOK); + assert_false(user_name_hint); + assert_non_null(certmap); + assert_non_null(certmap[0]); + assert_string_equal(certmap[0]->name, map_a.name); + assert_string_equal(certmap[0]->map_rule, map_a.map_rule); + assert_string_equal(certmap[0]->match_rule, map_a.match_rule); + assert_int_equal(certmap[0]->priority, map_a.priority); + assert_non_null(certmap[0]->domains); + assert_null(certmap[0]->domains[0]); + assert_null(certmap[1]); + check_certmap(certmap[0], &map_a, 0); + talloc_free(certmap); + + ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_b, true); + assert_int_equal(ret, EOK); + + ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, + &user_name_hint); + assert_int_equal(ret, EOK); + assert_true(user_name_hint); + assert_non_null(certmap); + assert_non_null(certmap[0]); + + check_certmap(certmap[0], &map_b, 3); + assert_null(certmap[1]); + talloc_free(certmap); + + ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_ab, false); + assert_int_equal(ret, EOK); + + ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, + &user_name_hint); + assert_int_equal(ret, EOK); + assert_false(user_name_hint); + assert_non_null(certmap); + assert_non_null(certmap[0]); + assert_non_null(certmap[1]); + assert_null(certmap[2]); + if (strcmp(certmap[0]->name, "map_a") == 0) { + check_certmap(certmap[0], &map_a, 0); + check_certmap(certmap[1], &map_b, 3); + } else { + check_certmap(certmap[0], &map_b, 3); + check_certmap(certmap[1], &map_a, 0); + } + talloc_free(certmap); + + ret = sysdb_update_certmap(ctctx->tctx->sysdb, certmap_c, false); + assert_int_equal(ret, EOK); + + ret = sysdb_get_certmap(ctctx, ctctx->tctx->sysdb, &certmap, + &user_name_hint); + assert_int_equal(ret, EOK); + assert_false(user_name_hint); + assert_non_null(certmap); + assert_non_null(certmap[0]); + check_certmap(certmap[0], &map_c, 3); + assert_null(certmap[1]); + talloc_free(certmap); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sysdb_get_certmap_not_exists, + test_sysdb_setup, + test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_update_certmap, + test_sysdb_setup, + test_sysdb_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, NULL); + test_dom_suite_setup(TESTS_PATH); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + if (rv == 0 && no_cleanup == 0) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, NULL); + } + return rv; +} diff --git a/src/tests/cmocka/test_sysdb_domain_resolution_order.c b/src/tests/cmocka/test_sysdb_domain_resolution_order.c new file mode 100644 index 0000000..a4d276b --- /dev/null +++ b/src/tests/cmocka/test_sysdb_domain_resolution_order.c @@ -0,0 +1,190 @@ +/* + SSSD + + sysdb_domain_resolution_order - Tests for domain resolution order calls + + Authors: + Fabiano Fidテェncio <fidencio@redhat.com> + + Copyright (C) 2017 Red Hat + + 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" +#include "db/sysdb_domain_resolution_order.h" +#include "db/sysdb_private.h" /* for sysdb->ldb member */ + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_sysdb_domain_resolution_order.ldb" + +#define TEST_DOM_NAME "test_sysdb_domain_resolution_order" + +#define TEST_ID_PROVIDER "ldap" + +struct domain_resolution_order_test_ctx { + struct sss_test_ctx *tctx; +}; + +static int test_sysdb_domain_resolution_order_setup(void **state) +{ + struct domain_resolution_order_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, + struct domain_resolution_order_test_ctx); + assert_non_null(test_ctx); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, TEST_DOM_NAME, + TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + *state = test_ctx; + return 0; +} + +static int test_sysdb_domain_resolution_order_teardown(void **state) +{ + struct domain_resolution_order_test_ctx *test_ctx = + talloc_get_type(*state, struct domain_resolution_order_test_ctx); + + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_sysdb_domain_resolution_order_ops(void **state) +{ + errno_t ret; + struct domain_resolution_order_test_ctx *test_ctx = + talloc_get_type(*state, struct domain_resolution_order_test_ctx); + const char *domains_in = NULL; + const char *domains_out = NULL; + struct ldb_dn *dn; + + dn = sysdb_domain_dn(test_ctx, test_ctx->tctx->dom); + assert_non_null(dn); + + /* Adding domainResolutionOrder for the first time */ + domains_in = "foo:bar:foobar"; + ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb, + dn, domains_in); + assert_int_equal(ret, EOK); + + ret = sysdb_get_domain_resolution_order(test_ctx, + test_ctx->tctx->dom->sysdb, dn, + &domains_out); + assert_int_equal(ret, EOK); + assert_true(strcmp(domains_in, domains_out) == 0); + + /* Setting the domainResolutionOrder to ":" ... + * + * It means, the domainResolutionOrder is set, but if there's another + * domainResolutionOrder with lower precedence those must be ignored. + */ + domains_in = ":"; + ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb, + dn, domains_in); + assert_int_equal(ret, EOK); + + ret = sysdb_get_domain_resolution_order(test_ctx, + test_ctx->tctx->dom->sysdb, dn, + &domains_out); + assert_int_equal(ret, EOK); + assert_true(strcmp(domains_in, domains_out) == 0); + + /* Changing the domainResolutionOrder */ + domains_in = "bar:foobar:foo"; + ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb, + dn, domains_in); + assert_int_equal(ret, EOK); + + ret = sysdb_get_domain_resolution_order(test_ctx, + test_ctx->tctx->dom->sysdb, dn, + &domains_out); + assert_int_equal(ret, EOK); + assert_true(strcmp(domains_in, domains_out) == 0); + + /* Removing the domainResolutionOrder attribute */ + domains_in = NULL; + ret = sysdb_update_domain_resolution_order(test_ctx->tctx->dom->sysdb, + dn, domains_in); + assert_int_equal(ret, EOK); + + ret = sysdb_get_domain_resolution_order(test_ctx, + test_ctx->tctx->dom->sysdb, dn, + &domains_out); + assert_int_equal(ret, ENOENT); + assert_true(domains_out == NULL); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sysdb_domain_resolution_order_ops, + test_sysdb_domain_resolution_order_setup, + test_sysdb_domain_resolution_order_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, NULL); + test_dom_suite_setup(TESTS_PATH); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + if (rv == 0 && no_cleanup == 0) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, NULL); + } + return rv; +} diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c new file mode 100644 index 0000000..d61cc14 --- /dev/null +++ b/src/tests/cmocka/test_sysdb_subdomains.c @@ -0,0 +1,644 @@ +/* + SSSD + + sysdb_subdomains - Tests for subdomains and related calls + + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2015 Red Hat + + 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "tests/common.h" +#include "db/sysdb_private.h" /* for sysdb->ldb member */ + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_sysdb_subdomains.ldb" + +#define TEST_DOM1_NAME "test_sysdb_subdomains_1" + +#define TEST_FLAT_NAME "TEST_1" +#define TEST_SID "S-1" +#define TEST_REALM "TEST_SYSDB_SUBDOMAINS" +#define TEST_FOREST TEST_REALM +#define TEST_ID_PROVIDER "ldap" + +#define TEST_DOM2_NAME "child2.test_sysdb_subdomains_2" +#define TEST_FLAT_NAME2 "CHILD2" +#define TEST_SID2 "S-2" +#define TEST_REALM2 "TEST_SYSDB_SUBDOMAINS2" +#define TEST_FOREST2 TEST_REALM2 + +const char *domains[] = { TEST_DOM1_NAME, + TEST_DOM2_NAME, + NULL }; + +struct subdom_test_ctx { + struct sss_test_ctx *tctx; +}; + +static int test_sysdb_subdom_setup(void **state) +{ + struct subdom_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, + struct subdom_test_ctx); + assert_non_null(test_ctx); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains, + TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + *state = test_ctx; + return 0; +} + +static int test_sysdb_subdom_teardown(void **state) +{ + struct subdom_test_ctx *test_ctx = + talloc_get_type(*state, struct subdom_test_ctx); + + test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_sysdb_subdomain_create(void **state) +{ + errno_t ret; + struct subdom_test_ctx *test_ctx = + talloc_get_type(*state, struct subdom_test_ctx); + + const char *const dom1[4] = { "dom1.sub", "DOM1.SUB", "dom1", "S-1" }; + const char *const dom2[4] = { "dom2.sub", "DOM2.SUB", "dom2", "S-2" }; + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom1[0], dom1[1], dom1[2], dom1[0], dom1[3], + MPG_DISABLED, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_non_null(test_ctx->tctx->dom->subdomains); + assert_string_equal(test_ctx->tctx->dom->subdomains->name, dom1[0]); + assert_int_equal(test_ctx->tctx->dom->subdomains->trust_direction, 0); + assert_true(test_ctx->tctx->dom->subdomains->mpg_mode == MPG_DISABLED); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom2[0], dom2[1], dom2[2], dom2[0], dom2[3], + MPG_DISABLED, false, NULL, 1, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_non_null(test_ctx->tctx->dom->subdomains->next); + assert_string_equal(test_ctx->tctx->dom->subdomains->next->name, dom2[0]); + assert_int_equal(test_ctx->tctx->dom->subdomains->next->trust_direction, 1); + assert_true(test_ctx->tctx->dom->subdomains->next->mpg_mode == MPG_DISABLED); + + /* Reverse the trust directions */ + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom1[0], dom1[1], dom1[2], dom1[0], dom1[3], + MPG_DISABLED, false, NULL, 1, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom2[0], dom2[1], dom2[2], dom2[0], dom2[3], + MPG_DISABLED, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_int_equal(test_ctx->tctx->dom->subdomains->trust_direction, 1); + assert_int_equal(test_ctx->tctx->dom->subdomains->next->trust_direction, 0); + + ret = sysdb_subdomain_delete(test_ctx->tctx->sysdb, dom2[0]); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_delete(test_ctx->tctx->sysdb, dom1[0]); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_int_equal(sss_domain_get_state(test_ctx->tctx->dom->subdomains), + DOM_DISABLED); + assert_int_equal( + sss_domain_get_state(test_ctx->tctx->dom->subdomains->next), + DOM_DISABLED); + + /* Test that changing the MPG status works */ + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom1[0], dom1[1], dom1[2], dom1[0], dom1[3], + MPG_ENABLED, false, NULL, 1, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom2[0], dom2[1], dom2[2], dom2[0], dom2[3], + MPG_ENABLED, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_true(test_ctx->tctx->dom->subdomains->mpg_mode == MPG_ENABLED); + assert_true(test_ctx->tctx->dom->subdomains->next->mpg_mode == MPG_ENABLED); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom1[0], dom1[1], dom1[2], dom1[0], dom1[3], + MPG_HYBRID, false, NULL, 1, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom2[0], dom2[1], dom2[2], dom2[0], dom2[3], + MPG_HYBRID, false, NULL, 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_true(test_ctx->tctx->dom->subdomains->mpg_mode == MPG_HYBRID); + assert_true(test_ctx->tctx->dom->subdomains->next->mpg_mode == MPG_HYBRID); +} + +static void test_sysdb_master_domain_ops(void **state) +{ + errno_t ret; + struct subdom_test_ctx *test_ctx = + talloc_get_type(*state, struct subdom_test_ctx); + + ret = sysdb_master_domain_add_info(test_ctx->tctx->dom, + "realm1", "flat1", "realm1", "id1", "forest1", + NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_master_domain_update(test_ctx->tctx->dom); + assert_int_equal(ret, EOK); + + assert_string_equal(test_ctx->tctx->dom->realm, "realm1"); + assert_string_equal(test_ctx->tctx->dom->flat_name, "flat1"); + assert_string_equal(test_ctx->tctx->dom->domain_id, "id1"); + assert_string_equal(test_ctx->tctx->dom->forest, "forest1"); + + ret = sysdb_master_domain_add_info(test_ctx->tctx->dom, + "realm2", "flat2", "realm2", "id2", "forest2", + NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_master_domain_update(test_ctx->tctx->dom); + assert_int_equal(ret, EOK); + + assert_string_equal(test_ctx->tctx->dom->realm, "realm2"); + assert_string_equal(test_ctx->tctx->dom->flat_name, "flat2"); + assert_string_equal(test_ctx->tctx->dom->domain_id, "id2"); + assert_string_equal(test_ctx->tctx->dom->forest, "forest2"); +} + +/* Parent domain totally separate from subdomains that imitate + * IPA domain and two forests + */ +static void test_sysdb_link_forest_root_ipa(void **state) +{ + errno_t ret; + struct subdom_test_ctx *test_ctx = + talloc_get_type(*state, struct subdom_test_ctx); + struct sss_domain_info *main_dom; + struct sss_domain_info *sub; + struct sss_domain_info *child; + + /* name, realm, flat, SID, forest */ + const char *const dom1[5] = { "dom1.sub", "DOM1.SUB", + "DOM1", "S-1", "DOM1.SUB" }; + const char *const child_dom1[5] = { "child1.dom1.sub", "CHILD1.DOM1.SUB", + "CHILD1.DOM1", "S-1-2", "DOM1.SUB" }; + const char *const dom2[5] = { "dom2.sub", "DOM2.SUB", + "DOM2", "S-2", "DOM2.SUB" }; + const char *const child_dom2[5] = { "child2.dom2.sub", "CHILD2.DOM1.SUB", + "CHILD2.DOM1", "S-2-2", "DOM2.SUB" }; + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom1[0], dom1[1], dom1[2], dom1[0], dom1[3], + MPG_DISABLED, false, dom1[4], 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + child_dom1[0], child_dom1[1], + child_dom1[2], child_dom1[0], child_dom1[3], + MPG_DISABLED, false, child_dom1[4], + 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + dom2[0], dom2[1], dom2[2], dom2[0], dom2[3], + MPG_DISABLED, false, dom2[4], + 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + child_dom2[0], child_dom2[1], + child_dom2[2], child_dom2[0], child_dom2[3], + MPG_DISABLED, false, child_dom2[4], + 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + /* Also update dom2 */ + ret = sysdb_update_subdomains(test_ctx->tctx->dom->next, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + sub = find_domain_by_name(test_ctx->tctx->dom, dom1[0], true); + assert_non_null(sub->forest_root); + assert_ptr_equal(sub->forest_root, sub); + + child = find_domain_by_name(test_ctx->tctx->dom, child_dom1[0], true); + assert_non_null(child->forest_root); + assert_ptr_equal(child->forest_root, sub); + + sub = find_domain_by_name(test_ctx->tctx->dom, dom2[0], true); + assert_non_null(sub->forest_root); + assert_ptr_equal(sub->forest_root, sub); + + child = find_domain_by_name(test_ctx->tctx->dom, child_dom2[0], true); + assert_non_null(child->forest_root); + assert_ptr_equal(child->forest_root, sub); + + main_dom = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM1_NAME, true); + assert_non_null(main_dom); + assert_non_null(main_dom->forest_root); + assert_true(main_dom->forest_root == main_dom); + + main_dom = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM2_NAME, true); + assert_non_null(main_dom); + assert_non_null(main_dom->forest_root); + assert_true(main_dom->forest_root == main_dom); +} + +/* Parent domain is an AD forest root and there are two subdomains + * child and parallel + */ +static void test_sysdb_link_forest_root_ad(void **state) +{ + errno_t ret; + struct subdom_test_ctx *test_ctx = + talloc_get_type(*state, struct subdom_test_ctx); + struct sss_domain_info *main_dom; + struct sss_domain_info *sub; + struct sss_domain_info *child; + + const char *const child_dom[5] = { "child.test_sysdb_subdomains",/* name */ + "CHILD.TEST_SYSDB_SUBDOMAINS",/* realm */ + "CHILD", /* flat */ + "S-1-2", /* sid */ + TEST_FOREST }; /* forest */ + + const char *const sub_dom[5] = { "another.subdomain", /* name */ + "ANOTHER.SUBDOMAIN", /* realm */ + "ANOTHER", /* flat */ + "S-1-3", /* sid */ + TEST_FOREST }; /* forest */ + + ret = sysdb_master_domain_add_info(test_ctx->tctx->dom, + TEST_REALM, + TEST_FLAT_NAME, + TEST_REALM, + TEST_SID, + TEST_FOREST, + NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + child_dom[0], child_dom[1], + child_dom[2], child_dom[0], child_dom[3], + MPG_DISABLED, false, child_dom[4], + 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + sub_dom[0], sub_dom[1], + sub_dom[2], sub_dom[0], sub_dom[3], + MPG_DISABLED, false, sub_dom[4], + 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + /* Also update dom2 */ + ret = sysdb_update_subdomains(test_ctx->tctx->dom->next, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + assert_non_null(test_ctx->tctx->dom->forest_root); + assert_true(test_ctx->tctx->dom->forest_root == test_ctx->tctx->dom); + assert_string_equal(test_ctx->tctx->dom->name, TEST_DOM1_NAME); + + child = find_domain_by_name(test_ctx->tctx->dom, child_dom[0], true); + assert_non_null(child->forest_root); + assert_ptr_equal(child->forest_root, test_ctx->tctx->dom); + + sub = find_domain_by_name(test_ctx->tctx->dom, sub_dom[0], true); + assert_non_null(sub->forest_root); + assert_ptr_equal(sub->forest_root, test_ctx->tctx->dom); + + /* Another separate domain is a forest of its own */ + main_dom = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM2_NAME, true); + assert_non_null(main_dom); + assert_non_null(main_dom->forest_root); + assert_true(main_dom->forest_root == main_dom); +} + +/* Parent domain is an AD member domain connected to a root domain + */ +static void test_sysdb_link_forest_member_ad(void **state) +{ + errno_t ret; + struct subdom_test_ctx *test_ctx = + talloc_get_type(*state, struct subdom_test_ctx); + struct sss_domain_info *main_dom; + struct sss_domain_info *sub; + struct sss_domain_info *root; + + const char *const forest_root[5] = { test_ctx->tctx->dom->name, /* name */ + TEST_FOREST, /* realm */ + TEST_FLAT_NAME, /* flat */ + TEST_SID, /* sid */ + TEST_FOREST }; /* forest */ + + const char *const child_dom[5] = { "child.test_sysdb_subdomains",/* name */ + "CHILD.TEST_SYSDB_SUBDOMAINS",/* realm */ + "CHILD", /* flat */ + "S-1-2", /* sid */ + TEST_FOREST }; /* forest */ + + const char *const sub_dom[5] = { "another.subdomain", /* name */ + "ANOTHER.SUBDOMAIN", /* realm */ + "ANOTHER", /* flat */ + "S-1-3", /* sid */ + TEST_FOREST }; /* forest */ + + ret = sysdb_master_domain_add_info(test_ctx->tctx->dom, + child_dom[1], + child_dom[2], + child_dom[0], + child_dom[3], + TEST_FOREST, + NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + sub_dom[0], sub_dom[1], + sub_dom[2], sub_dom[0], sub_dom[3], + MPG_DISABLED, false, sub_dom[4], + 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(test_ctx->tctx->sysdb, + forest_root[0], forest_root[1], + forest_root[2], forest_root[0], forest_root[3], + MPG_DISABLED, false, forest_root[4], + 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_master_domain_update(test_ctx->tctx->dom); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom, test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + /* Also update dom2 */ + ret = sysdb_master_domain_update(test_ctx->tctx->dom->next); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(test_ctx->tctx->dom->next, + test_ctx->tctx->confdb); + assert_int_equal(ret, EOK); + + /* Checks */ + root = find_domain_by_name(test_ctx->tctx->dom, forest_root[0], true); + assert_non_null(root->forest_root); + assert_ptr_equal(root->forest_root, root); + + assert_non_null(test_ctx->tctx->dom->forest_root); + assert_true(test_ctx->tctx->dom->forest_root == root); + + sub = find_domain_by_name(test_ctx->tctx->dom, sub_dom[0], true); + assert_non_null(sub->forest_root); + assert_ptr_equal(sub->forest_root, root); + + /* Another separate domain is a forest of its own */ + main_dom = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM2_NAME, true); + assert_non_null(main_dom); + assert_non_null(main_dom->forest_root); + assert_true(main_dom->forest_root == main_dom); +} + + +/* Each parent domain has a subdomain. One parent domain is a root domain, + * the other is not. + */ +static void test_sysdb_link_ad_multidom(void **state) +{ + errno_t ret; + struct subdom_test_ctx *test_ctx = + talloc_get_type(*state, struct subdom_test_ctx); + struct sss_domain_info *main_dom1; + struct sss_domain_info *main_dom2; + struct sss_domain_info *root; + + const char *const child_dom[5] = { "child.test_sysdb_subdomains",/* name */ + "CHILD.TEST_SYSDB_SUBDOMAINS",/* realm */ + "CHILD", /* flat */ + "S-1-2", /* sid */ + TEST_FOREST }; /* forest */ + + const char *const dom2_forest_root[5] = \ + { "test_sysdb_subdomains_2", /* name */ + TEST_FOREST2, /* realm */ + "TEST2", /* flat */ + TEST_SID2, /* sid */ + TEST_FOREST2 }; /* forest */ + + + main_dom1 = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM1_NAME, true); + main_dom2 = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM2_NAME, true); + + ret = sysdb_master_domain_add_info(main_dom1, + TEST_REALM, + TEST_FLAT_NAME, + TEST_REALM, + TEST_SID, + TEST_FOREST, + NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(main_dom1->sysdb, + child_dom[0], child_dom[1], + child_dom[2], child_dom[0], child_dom[3], + MPG_DISABLED, false, child_dom[4], + 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_master_domain_update(main_dom1); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(main_dom1, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_master_domain_add_info(main_dom2, + TEST_REALM2, + TEST_FLAT_NAME2, + TEST_REALM2, + TEST_SID2, + TEST_FOREST2, + NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_subdomain_store(main_dom2->sysdb, + dom2_forest_root[0], dom2_forest_root[1], + dom2_forest_root[2], dom2_forest_root[0], dom2_forest_root[3], + MPG_DISABLED, false, dom2_forest_root[4], 0, NULL); + assert_int_equal(ret, EOK); + + ret = sysdb_master_domain_update(main_dom2); + assert_int_equal(ret, EOK); + + ret = sysdb_update_subdomains(main_dom2, NULL); + assert_int_equal(ret, EOK); + + main_dom1 = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM1_NAME, true); + assert_non_null(main_dom1); + assert_non_null(main_dom1->forest_root); + assert_true(main_dom1->forest_root == main_dom1); + + main_dom2 = find_domain_by_name(test_ctx->tctx->dom, TEST_DOM2_NAME, true); + assert_non_null(main_dom1); + assert_non_null(main_dom1->forest_root); + assert_true(main_dom1->forest_root == main_dom1); + + root = find_domain_by_name(test_ctx->tctx->dom, dom2_forest_root[0], true); + assert_non_null(root); + assert_non_null(root->forest_root); + assert_ptr_equal(root->forest_root, main_dom2); + +} + +static void test_sysdb_set_and_get_site(void **state) +{ + TALLOC_CTX *tmp_ctx; + struct subdom_test_ctx *test_ctx = + talloc_get_type(*state, struct subdom_test_ctx); + const char *site; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + assert_non_null(test_ctx); + + ret = sysdb_get_site(test_ctx, test_ctx->tctx->dom, &site); + assert_int_equal(ret, EOK); + assert_null(site); + + ret = sysdb_set_site(test_ctx->tctx->dom, "TestSite"); + assert_int_equal(ret, EOK); + + ret = sysdb_get_site(tmp_ctx, test_ctx->tctx->dom, &site); + assert_int_equal(ret, EOK); + assert_string_equal(site, "TestSite"); + + talloc_free(tmp_ctx); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sysdb_master_domain_ops, + test_sysdb_subdom_setup, + test_sysdb_subdom_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_subdomain_create, + test_sysdb_subdom_setup, + test_sysdb_subdom_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_link_forest_root_ipa, + test_sysdb_subdom_setup, + test_sysdb_subdom_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_link_forest_root_ad, + test_sysdb_subdom_setup, + test_sysdb_subdom_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_link_forest_member_ad, + test_sysdb_subdom_setup, + test_sysdb_subdom_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_link_ad_multidom, + test_sysdb_subdom_setup, + test_sysdb_subdom_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_set_and_get_site, + test_sysdb_subdom_setup, + test_sysdb_subdom_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, NULL); + test_dom_suite_setup(TESTS_PATH); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + if (rv == 0 && no_cleanup == 0) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, NULL); + } + return rv; +} diff --git a/src/tests/cmocka/test_sysdb_sudo.c b/src/tests/cmocka/test_sysdb_sudo.c new file mode 100644 index 0000000..b8ec53f --- /dev/null +++ b/src/tests/cmocka/test_sysdb_sudo.c @@ -0,0 +1,1085 @@ +/* + Authors: + Petr ト憩ch <pcech@redhat.com> + + Copyright (C) 2016 Red Hat + + 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> +#include <ldb_module.h> + +#include "tests/cmocka/common_mock.h" +#include "src/db/sysdb_sudo.h" +#include "src/db/sysdb_private.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_sysdb_sudorules.ldb" +#define TEST_DOM_NAME "test_domain.test" + +#define TEST_CACHE_SUDO_TIMEOUT "20" + +#define TEST_USER_NON_EXIST "no_user" + +#define TEST_GROUP_NAME "test_sudo_group" +#define TEST_GID 10001 + +#define OVERRIDE_USER_NAME "user_test" +#define OVERRIDE_GROUP_NAME "group_sudo_test" +#define OVERRIDE_UID 2112 + +/* sysdb_sudo_convert_time function is static */ +extern char *strptime(const char *__restrict __s, + const char *__restrict __fmt, + struct tm *__tp); +#include "src/db/sysdb_sudo.c" + +struct test_user { + const char *name; + uid_t uid; + gid_t gid; +} users[] = { { "test_USER1", 1001, 1001 }, + { "test_user2", 1002, 1002 }, + { "test_user3", 1003, 1003 } }; + +struct test_rule { + const char *name; + const char *host; + const char *as_user; +} rules[] = { { "test_rule1", "test_host1.test_domain.test", "root" }, + { "test_rule2", "test_host2.test_domain.test", "root" }, + { "test_rule3", "test_host3.test_domain.test", "root" } }; + +struct sysdb_test_ctx { + struct sss_test_ctx *tctx; +}; + +static void create_groups(struct sss_domain_info *domain) +{ + errno_t ret; + + ret = sysdb_add_group(domain, TEST_GROUP_NAME, TEST_GID, + NULL, 30, time(NULL)); + assert_int_equal(ret, EOK); +} + +static void create_users(struct sss_domain_info *domain) +{ + errno_t ret; + int gid; + + for (int i = 0; i < 3; i++) { + gid = (i == 0) ? 0 : TEST_GID; + ret = sysdb_add_user(domain, users[i].name, users[i].uid, gid, + users[i].name, NULL, "/bin/bash", domain->name, + NULL, 30, time(NULL)); + assert_int_equal(ret, EOK); + } +} + +static void create_rule_attrs(struct sysdb_attrs *rule, int i) +{ + errno_t ret; + + ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_CN, + rules[i].name); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_HOST, + rules[i].host); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_RUNASUSER, + rules[i].as_user); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_USER, + users[i].name); + assert_int_equal(ret, EOK); +} + +static void create_rule_attrs_multiple_sudoUser(struct sysdb_attrs *rule) +{ + errno_t ret; + + ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_CN, + rules[0].name); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_HOST, + rules[0].host); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_RUNASUSER, + rules[0].as_user); + assert_int_equal(ret, EOK); + + for (int i = 0; i < 3; i++ ) { + ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_USER, + users[i].name); + assert_int_equal(ret, EOK); + } +} + +static int get_stored_rules_count(struct sysdb_test_ctx *test_ctx) +{ + errno_t ret; + const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, NULL }; + struct ldb_message **msgs = NULL; + size_t msgs_count; + + ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, + "(objectClass=sudoRule)", + attrs, &msgs_count, &msgs); + if (!(ret == EOK || ret == ENOENT)) { + msgs_count = -1; + } + talloc_zfree(msgs); + + return msgs_count; +} + +static int test_sysdb_setup(void **state) +{ + struct sysdb_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct sysdb_test_ctx); + assert_non_null(test_ctx); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, "ipa", NULL); + assert_non_null(test_ctx->tctx); + + create_groups(test_ctx->tctx->dom); + create_users(test_ctx->tctx->dom); + + reset_ldb_errstrings(test_ctx->tctx->dom); + check_leaks_push(test_ctx); + + *state = (void *)test_ctx; + return 0; +} + +static int test_sysdb_teardown(void **state) +{ + struct sysdb_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct sysdb_test_ctx); + + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + + reset_ldb_errstrings(test_ctx->tctx->dom); + assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + assert_true(leak_check_teardown()); + + return 0; +} + +static int test_sysdb_views_setup(void **state) +{ + struct sysdb_test_ctx *test_ctx; + errno_t ret; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct sysdb_test_ctx); + assert_non_null(test_ctx); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH, TEST_CONF_DB, + TEST_DOM_NAME, "ipa", NULL); + assert_non_null(test_ctx->tctx); + + create_groups(test_ctx->tctx->dom); + create_users(test_ctx->tctx->dom); + + ret = sysdb_update_view_name(test_ctx->tctx->dom->sysdb, SYSDB_LOCAL_VIEW_NAME); + assert_int_equal(ret, EOK); + sysdb_master_domain_update(test_ctx->tctx->dom); + + reset_ldb_errstrings(test_ctx->tctx->dom); + check_leaks_push(test_ctx); + + *state = (void *)test_ctx; + return 0; +} + +static int test_sysdb_views_teardown(void **state) +{ + struct sysdb_test_ctx *test_ctx; + + test_ctx = talloc_get_type_abort(*state, struct sysdb_test_ctx); + + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + + reset_ldb_errstrings(test_ctx->tctx->dom); + assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + assert_true(leak_check_teardown()); + + return 0; +} + +void test_store_sudo(void **state) +{ + errno_t ret; + char *filter; + const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, SYSDB_SUDO_CACHE_AT_HOST, + SYSDB_SUDO_CACHE_AT_RUNASUSER, + SYSDB_SUDO_CACHE_AT_USER, NULL }; + struct ldb_message **msgs = NULL; + size_t msgs_count; + const char *result; + struct sysdb_attrs *rule; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + rule = sysdb_new_attrs(test_ctx); + assert_non_null(rule); + create_rule_attrs(rule, 0); + + ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1); + assert_int_equal(ret, EOK); + + filter = sysdb_sudo_filter_user(test_ctx, users[0].name, NULL, 0); + assert_non_null(filter); + + ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter, + attrs, &msgs_count, &msgs); + assert_int_equal(ret, EOK); + + assert_int_equal(msgs_count, 1); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_CN, NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].name); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_HOST, + NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].host); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_RUNASUSER, + NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].as_user); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_USER, + NULL); + assert_non_null(result); + assert_string_equal(result, users[0].name); + + talloc_zfree(rule); + talloc_zfree(filter); + talloc_zfree(msgs); +} + +void test_store_sudo_case_sensitive(void **state) +{ + errno_t ret; + char *filter; + const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, SYSDB_SUDO_CACHE_AT_HOST, + SYSDB_SUDO_CACHE_AT_RUNASUSER, + SYSDB_SUDO_CACHE_AT_USER, NULL }; + struct ldb_message **msgs = NULL; + size_t msgs_count; + const char *result; + struct ldb_message_element *element; + struct sysdb_attrs *rule; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + const char *lowered_name = sss_tc_utf8_str_tolower(test_ctx, users[0].name); + + rule = sysdb_new_attrs(test_ctx); + assert_non_null(rule); + create_rule_attrs_multiple_sudoUser(rule); + + test_ctx->tctx->dom->case_sensitive = true; + + ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1); + assert_int_equal(ret, EOK); + + filter = sysdb_sudo_filter_user(test_ctx, users[0].name, NULL, 0); + assert_non_null(filter); + + ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter, + attrs, &msgs_count, &msgs); + assert_int_equal(ret, EOK); + + assert_int_equal(msgs_count, 1); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_CN, NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].name); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_HOST, + NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].host); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_RUNASUSER, + NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].as_user); + + ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER, + users[0].name); + assert_int_equal(ret, 1); + + ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER, + lowered_name); + assert_int_equal(ret, 0); + + ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER, + users[1].name); + assert_int_equal(ret, 1); + + ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER, + users[2].name); + assert_int_equal(ret, 1); + + element = ldb_msg_find_element(msgs[0], SYSDB_SUDO_CACHE_AT_USER); + assert_int_equal(element->num_values, 3); + + talloc_zfree(lowered_name); + talloc_zfree(rule); + talloc_zfree(filter); + talloc_zfree(msgs); +} + +void test_store_sudo_case_insensitive(void **state) +{ + errno_t ret; + char *filter; + const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, SYSDB_SUDO_CACHE_AT_HOST, + SYSDB_SUDO_CACHE_AT_RUNASUSER, + SYSDB_SUDO_CACHE_AT_USER, NULL }; + struct ldb_message **msgs = NULL; + size_t msgs_count; + const char *result; + struct ldb_message_element *element; + struct sysdb_attrs *rule; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + const char *lowered_name = sss_tc_utf8_str_tolower(test_ctx, users[0].name); + + rule = sysdb_new_attrs(test_ctx); + assert_non_null(rule); + create_rule_attrs_multiple_sudoUser(rule); + + test_ctx->tctx->dom->case_sensitive = false; + + ret = sysdb_attrs_add_lower_case_string(rule, false, + SYSDB_SUDO_CACHE_AT_USER, + users[0].name); + assert_int_equal(ret, EOK); + + ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1); + assert_int_equal(ret, EOK); + + filter = sysdb_sudo_filter_user(test_ctx, users[0].name, NULL, 0); + assert_non_null(filter); + + ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter, + attrs, &msgs_count, &msgs); + assert_int_equal(ret, EOK); + + assert_int_equal(msgs_count, 1); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_CN, NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].name); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_HOST, + NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].host); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_RUNASUSER, + NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].as_user); + + for (int i = 0; i < 3; i++) { + ret = ldb_msg_check_string_attribute(msgs[0], SYSDB_SUDO_CACHE_AT_USER, + users[i].name); + assert_int_equal(ret, 1); + } + + /* test there is no duplication of lowercase forms */ + element = ldb_msg_find_element(msgs[0], SYSDB_SUDO_CACHE_AT_USER); + assert_int_equal(element->num_values, 4); + + talloc_zfree(lowered_name); + talloc_zfree(rule); + talloc_zfree(filter); + talloc_zfree(msgs); +} + +void test_sudo_purge_by_filter(void **state) +{ + errno_t ret; + struct sysdb_attrs *rule; + char *delete_filter; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + rule = sysdb_new_attrs(test_ctx); + assert_non_null(rule); + create_rule_attrs(rule, 0); + + ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1); + assert_int_equal(ret, EOK); + assert_int_equal(get_stored_rules_count(test_ctx), 1); + + delete_filter = sysdb_sudo_filter_user(test_ctx, users[0].name, NULL, 0); + assert_non_null(delete_filter); + + ret = sysdb_sudo_purge(test_ctx->tctx->dom, delete_filter, NULL, 0); + assert_int_equal(ret, EOK); + assert_int_equal(get_stored_rules_count(test_ctx), 0); + + talloc_zfree(rule); + talloc_zfree(delete_filter); +} + +void test_sudo_purge_by_rules(void **state) +{ + errno_t ret; + struct sysdb_attrs *rule; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + rule = sysdb_new_attrs(test_ctx); + assert_non_null(rule); + create_rule_attrs(rule, 0); + + ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1); + assert_int_equal(ret, EOK); + assert_int_equal(get_stored_rules_count(test_ctx), 1); + + ret = sysdb_sudo_purge(test_ctx->tctx->dom, NULL, &rule, 1); + assert_int_equal(ret, EOK); + assert_int_equal(get_stored_rules_count(test_ctx), 0); + + talloc_zfree(rule); +} + +void test_sudo_set_get_last_full_refresh(void **state) +{ + errno_t ret; + time_t now; + time_t loaded_time; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + now = time(NULL); + ret = sysdb_sudo_set_last_full_refresh(test_ctx->tctx->dom, now); + assert_int_equal(ret, EOK); + + ret = sysdb_sudo_get_last_full_refresh(test_ctx->tctx->dom, &loaded_time); + assert_int_equal(ret, EOK); + assert_int_equal(now, loaded_time); +} + +void test_get_sudo_user_info(void **state) +{ + errno_t ret; + char **groupnames = NULL; + const char *orig_username; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + /* User 1 has group. */ + ret = sysdb_get_sudo_user_info(test_ctx, test_ctx->tctx->dom, users[1].name, + &orig_username, NULL, &groupnames); + assert_int_equal(ret, EOK); + assert_string_equal(groupnames[0], TEST_GROUP_NAME); + assert_string_equal(orig_username, users[1].name); + + talloc_zfree(groupnames); + talloc_zfree(orig_username); +} + +void test_get_overriden_sudo_user_info(void **state) +{ + errno_t ret; + char **groupnames = NULL; + const char *orig_username; + uid_t orig_uid; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + char *strdn; + char *safe_dn; + char *anchor; + char *group_fqname; + char *user_fqname; + struct sysdb_attrs *attrs; + struct ldb_dn *ldb_dn; + + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + /* Override user's name and primary UID */ + user_fqname = sss_create_internal_fqname(test_ctx, + OVERRIDE_USER_NAME, + test_ctx->tctx->dom->name); + assert_non_null(user_fqname); + + ldb_dn = sysdb_user_dn(attrs, test_ctx->tctx->dom, users[1].name); + assert_non_null(ldb_dn); + strdn = sysdb_user_strdn(attrs, test_ctx->tctx->dom->name, users[1].name); + assert_non_null(strdn); + ret = sysdb_dn_sanitize(attrs, strdn, &safe_dn); + assert_int_equal(ret, EOK); + anchor = talloc_asprintf(attrs, ":%s:%s", SYSDB_LOCAL_VIEW_NAME, safe_dn); + assert_non_null(anchor); + + ret = sysdb_attrs_add_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID, anchor); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, user_fqname); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_uint32(attrs, SYSDB_UIDNUM, OVERRIDE_UID); + assert_int_equal(ret, EOK); + + ret = sysdb_store_override(test_ctx->tctx->dom, SYSDB_LOCAL_VIEW_NAME, + SYSDB_MEMBER_USER, attrs, ldb_dn); + assert_int_equal(ret, EOK); + talloc_zfree(attrs); + + /* Override user's secondary group name */ + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + group_fqname = sss_create_internal_fqname(test_ctx, + OVERRIDE_GROUP_NAME, + test_ctx->tctx->dom->name); + assert_non_null(group_fqname); + + ldb_dn = sysdb_group_dn(attrs, test_ctx->tctx->dom, TEST_GROUP_NAME); + assert_non_null(ldb_dn); + strdn = sysdb_group_strdn(attrs, test_ctx->tctx->dom->name, TEST_GROUP_NAME); + assert_non_null(strdn); + ret = sysdb_dn_sanitize(attrs, strdn, &safe_dn); + assert_int_equal(ret, EOK); + anchor = talloc_asprintf(attrs, ":%s:%s", SYSDB_LOCAL_VIEW_NAME, safe_dn); + assert_non_null(anchor); + + ret = sysdb_attrs_add_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID, anchor); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, group_fqname); + assert_int_equal(ret, EOK); + + ret = sysdb_store_override(test_ctx->tctx->dom, SYSDB_LOCAL_VIEW_NAME, + SYSDB_MEMBER_GROUP, attrs, ldb_dn); + assert_int_equal(ret, EOK); + + /* User must be searchable by their overridden name */ + ret = sysdb_get_sudo_user_info(test_ctx, test_ctx->tctx->dom, user_fqname, + &orig_username, &orig_uid, &groupnames); + assert_int_equal(ret, EOK); + + /* sysdb_get_sudo_user_info must return the original values, not the + * overridden one */ + assert_string_equal(groupnames[0], TEST_GROUP_NAME); + assert_string_equal(orig_username, users[1].name); + assert_int_equal(orig_uid, users[1].uid); + + talloc_zfree(groupnames); + talloc_zfree(orig_username); + talloc_zfree(attrs); + talloc_zfree(user_fqname); + talloc_zfree(group_fqname); +} + +void test_get_sudo_user_info_nogroup(void **state) +{ + errno_t ret; + char **groupnames = NULL; + const char *orig_username; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + /* User 0 hasn't group. */ + ret = sysdb_get_sudo_user_info(test_ctx, test_ctx->tctx->dom, users[0].name, + &orig_username, NULL, &groupnames); + assert_int_equal(ret, EOK); + assert_null(groupnames); + assert_string_equal(orig_username, users[0].name); + + talloc_zfree(groupnames); + talloc_zfree(orig_username); +} + +void test_get_sudo_nouser(void **state) +{ + errno_t ret; + char **groupnames = NULL; + const char *orig_username = NULL; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + ret = sysdb_get_sudo_user_info(test_ctx, test_ctx->tctx->dom, + TEST_USER_NON_EXIST, + &orig_username, NULL, &groupnames); + assert_int_equal(ret, ENOENT); + assert_null(orig_username); + assert_null(groupnames); +} + +void test_set_sudo_rule_attr_add(void **state) +{ + errno_t ret; + struct sysdb_attrs *rule; + struct sysdb_attrs *new_rule; + const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, SYSDB_SUDO_CACHE_AT_COMMAND, + NULL }; + char *filter; + struct ldb_message **msgs = NULL; + size_t msgs_count; + const char *result; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + rule = sysdb_new_attrs(test_ctx); + assert_non_null(rule); + create_rule_attrs(rule, 0); + + ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1); + assert_int_equal(ret, EOK); + assert_int_equal(get_stored_rules_count(test_ctx), 1); + + new_rule = sysdb_new_attrs(test_ctx); + assert_non_null(new_rule); + ret = sysdb_attrs_add_string(new_rule, SYSDB_SUDO_CACHE_AT_COMMAND, + "test_command"); + assert_int_equal(ret, EOK); + + ret = sysdb_set_sudo_rule_attr(test_ctx->tctx->dom, rules[0].name, + new_rule, SYSDB_MOD_ADD); + assert_int_equal(ret, EOK); + + filter = sysdb_sudo_filter_user(test_ctx, users[0].name, NULL, 0); + assert_non_null(filter); + + ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter, + attrs, &msgs_count, &msgs); + assert_int_equal(ret, EOK); + assert_int_equal(msgs_count, 1); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_CN, NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].name); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_COMMAND, + NULL); + assert_non_null(result); + assert_string_equal(result, "test_command"); + + talloc_zfree(rule); + talloc_zfree(new_rule); + talloc_zfree(filter); + talloc_zfree(msgs); +} + +void test_set_sudo_rule_attr_replace(void **state) +{ + errno_t ret; + struct sysdb_attrs *rule; + struct sysdb_attrs *new_rule; + const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, SYSDB_CACHE_EXPIRE, NULL }; + char *filter; + struct ldb_message **msgs = NULL; + size_t msgs_count; + const char *result; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + rule = sysdb_new_attrs(test_ctx); + assert_non_null(rule); + create_rule_attrs(rule, 0); + + ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1); + assert_int_equal(ret, EOK); + assert_int_equal(get_stored_rules_count(test_ctx), 1); + + new_rule = sysdb_new_attrs(test_ctx); + assert_non_null(new_rule); + ret = sysdb_attrs_add_time_t(new_rule, SYSDB_CACHE_EXPIRE, 10); + assert_int_equal(ret, EOK); + + ret = sysdb_set_sudo_rule_attr(test_ctx->tctx->dom, rules[0].name, + new_rule, SYSDB_MOD_REP); + assert_int_equal(ret, EOK); + + filter = sysdb_sudo_filter_user(test_ctx, users[0].name, NULL, 0); + assert_non_null(filter); + + ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter, + attrs, &msgs_count, &msgs); + assert_int_equal(ret, EOK); + assert_int_equal(msgs_count, 1); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_CN, NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].name); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_CACHE_EXPIRE, NULL); + assert_non_null(result); + assert_string_equal(result, "10"); + + talloc_zfree(rule); + talloc_zfree(new_rule); + talloc_zfree(filter); + talloc_zfree(msgs); +} + +void test_set_sudo_rule_attr_delete(void **state) +{ + errno_t ret; + struct sysdb_attrs *rule; + struct sysdb_attrs *new_rule; + const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, SYSDB_SUDO_CACHE_AT_HOST, + NULL }; + char *filter; + struct ldb_message **msgs = NULL; + size_t msgs_count; + const char *result; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + rule = sysdb_new_attrs(test_ctx); + assert_non_null(rule); + create_rule_attrs(rule, 0); + + ret = sysdb_sudo_store(test_ctx->tctx->dom, &rule, 1); + assert_int_equal(ret, EOK); + assert_int_equal(get_stored_rules_count(test_ctx), 1); + + new_rule = sysdb_new_attrs(test_ctx); + assert_non_null(new_rule); + ret = sysdb_attrs_add_string(new_rule, SYSDB_SUDO_CACHE_AT_HOST, + rules[0].host); + assert_int_equal(ret, EOK); + + ret = sysdb_set_sudo_rule_attr(test_ctx->tctx->dom, rules[0].name, + new_rule, LDB_FLAG_MOD_DELETE); + assert_int_equal(ret, EOK); + + filter = sysdb_sudo_filter_user(test_ctx, users[0].name, NULL, 0); + assert_non_null(filter); + + ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter, + attrs, &msgs_count, &msgs); + assert_int_equal(ret, EOK); + assert_int_equal(msgs_count, 1); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_CN, NULL); + assert_non_null(result); + assert_string_equal(result, rules[0].name); + + result = ldb_msg_find_attr_as_string(msgs[0], SYSDB_SUDO_CACHE_AT_HOST, + "deleted"); + assert_non_null(result); + assert_string_equal(result, "deleted"); + + talloc_zfree(rule); + talloc_zfree(new_rule); + talloc_zfree(filter); + talloc_zfree(msgs); +} + +void test_search_sudo_rules(void **state) +{ + errno_t ret; + const char *filter; + const char *attrs[] = { SYSDB_NAME, NULL }; + struct ldb_message **msgs = NULL; + size_t msgs_count; + size_t num_rules = 2; + struct sysdb_attrs *tmp_rules[num_rules]; + const char *rule_names[num_rules]; + const char *db_results[num_rules]; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + tmp_rules[0] = sysdb_new_attrs(test_ctx); + assert_non_null(tmp_rules[0]); + create_rule_attrs(tmp_rules[0], 0); + + tmp_rules[1] = sysdb_new_attrs(test_ctx); + assert_non_null(tmp_rules[1]); + create_rule_attrs(tmp_rules[1], 1); + + ret = sysdb_sudo_store(test_ctx->tctx->dom, tmp_rules, 2); + assert_int_equal(ret, EOK); + assert_int_equal(get_stored_rules_count(test_ctx), 2); + + filter = "(objectClass=" SYSDB_SUDO_CACHE_OC ")"; + + ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter, + attrs, &msgs_count, &msgs); + assert_int_equal(ret, EOK); + + assert_int_equal(msgs_count, 2); + + rule_names[0] = rules[0].name; + rule_names[1] = rules[1].name; + + for (int i = 0; i < num_rules; ++i) { + db_results[i] = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL); + assert_non_null(db_results[i]); + } + + assert_string_not_equal(db_results[0], db_results[1]); + assert_true(are_values_in_array(rule_names, num_rules, + db_results, num_rules)); + + talloc_zfree(tmp_rules[0]); + talloc_zfree(tmp_rules[1]); + talloc_zfree(msgs); +} + +void test_filter_rules_by_time(void **state) +{ + errno_t ret; + time_t cur_time; + struct sysdb_attrs *tmp_attr; + uint32_t _num_rules; + struct sysdb_attrs *tmp_rules[2]; + struct sysdb_attrs **_rules; + struct sysdb_attrs **loaded_rules; + size_t msgs_count; + struct ldb_message **msgs = NULL; + char buff[20]; + const char *attrs[] = { SYSDB_SUDO_CACHE_AT_CN, SYSDB_SUDO_CACHE_AT_HOST, + SYSDB_SUDO_CACHE_AT_RUNASUSER, + SYSDB_SUDO_CACHE_AT_USER, + SYSDB_IPA_SUDORULE_NOTBEFORE, + SYSDB_IPA_SUDORULE_NOTAFTER, NULL }; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + tmp_rules[0] = sysdb_new_attrs(test_ctx); + assert_non_null(tmp_rules[0]); + create_rule_attrs(tmp_rules[0], 0); + + tmp_rules[1] = sysdb_new_attrs(test_ctx); + assert_non_null(tmp_rules[1]); + create_rule_attrs(tmp_rules[1], 1); + + ret = sysdb_sudo_store(test_ctx->tctx->dom, tmp_rules, 2); + assert_int_equal(ret, EOK); + assert_int_equal(get_stored_rules_count(test_ctx), 2); + + /* + * We hit DST issue of time functions, + * so we use big time shift to avoid this. + */ + + tmp_attr = sysdb_new_attrs(test_ctx); + assert_non_null(tmp_attr); + cur_time = time(NULL) + 10000; + strftime(buff, 20, "%Y%m%d%H%M%S%z", localtime(&cur_time)); + ret = sysdb_attrs_add_string(tmp_attr, SYSDB_SUDO_CACHE_AT_NOTBEFORE, buff); + assert_int_equal(ret, EOK); + cur_time = time(NULL) + 20000; + strftime(buff, 20, "%Y%m%d%H%M%S%z", localtime(&cur_time)); + ret = sysdb_attrs_add_string(tmp_attr, SYSDB_SUDO_CACHE_AT_NOTAFTER, buff); + assert_int_equal(ret, EOK); + ret = sysdb_set_sudo_rule_attr(test_ctx->tctx->dom, rules[0].name, + tmp_attr, SYSDB_MOD_ADD); + assert_int_equal(ret, EOK); + talloc_zfree(tmp_attr); + + tmp_attr = sysdb_new_attrs(test_ctx); + assert_non_null(tmp_attr); + cur_time = time(NULL) - 10000; + strftime(buff, 20, "%Y%m%d%H%M%S%z", localtime(&cur_time)); + ret = sysdb_attrs_add_string(tmp_attr, SYSDB_SUDO_CACHE_AT_NOTBEFORE, buff); + assert_int_equal(ret, EOK); + cur_time = time(NULL) + 10000; + strftime(buff, 20, "%Y%m%d%H%M%S%z", localtime(&cur_time)); + ret = sysdb_attrs_add_string(tmp_attr, SYSDB_SUDO_CACHE_AT_NOTAFTER, buff); + assert_int_equal(ret, EOK); + ret = sysdb_set_sudo_rule_attr(test_ctx->tctx->dom, rules[1].name, + tmp_attr, SYSDB_MOD_ADD); + assert_int_equal(ret, EOK); + talloc_zfree(tmp_attr); + + assert_int_equal(get_stored_rules_count(test_ctx), 2); + + ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, + "(objectClass=sudoRule)", + attrs, &msgs_count, &msgs); + assert_int_equal(ret, EOK); + assert_int_equal(msgs_count, 2); + + ret = sysdb_msg2attrs(test_ctx, 2, msgs, &loaded_rules); + assert_int_equal(ret, EOK); + + talloc_zfree(msgs); + + ret = sysdb_sudo_filter_rules_by_time(test_ctx, 2, loaded_rules, 0, + &_num_rules, &_rules); + + assert_int_equal(ret, EOK); + assert_int_equal(_num_rules, 1); + + talloc_zfree(tmp_rules[0]); + talloc_zfree(tmp_rules[1]); + talloc_zfree(loaded_rules); + talloc_zfree(_rules); +} + +void test_sudo_convert_time(void **state) +{ + /* Each ctime should map to its corresponding utime */ + const char *ctimes[] = {"20220715090000Z", + "20220715090000+0200", + "20220715090000-0200"}; + const time_t utimes[] = {1657875600, + 1657868400, + 1657882800}; + const int ntimes = sizeof(ctimes) / sizeof(ctimes[0]); + time_t converted; + errno_t ret; + + for (int i = 0; i < ntimes; i++) { + ret = sysdb_sudo_convert_time(ctimes[i], &converted); + assert_int_equal(ret, EOK); + assert_int_equal(converted, utimes[i]); + } +} + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + /* sysdb_sudo_store() */ + cmocka_unit_test_setup_teardown(test_store_sudo, + test_sysdb_setup, + test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_store_sudo_case_sensitive, + test_sysdb_setup, + test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_store_sudo_case_insensitive, + test_sysdb_setup, + test_sysdb_teardown), + + /* sysdb_sudo_purge() */ + cmocka_unit_test_setup_teardown(test_sudo_purge_by_filter, + test_sysdb_setup, + test_sysdb_teardown), + + cmocka_unit_test_setup_teardown(test_sudo_purge_by_rules, + test_sysdb_setup, + test_sysdb_teardown), + + /* + * sysdb_sudo_set_last_full_refresh() + * sysdb_sudo_get_last_full_refresh() + */ + cmocka_unit_test_setup_teardown(test_sudo_set_get_last_full_refresh, + test_sysdb_setup, + test_sysdb_teardown), + + /* sysdb_get_sudo_user_info() */ + cmocka_unit_test_setup_teardown(test_get_sudo_user_info, + test_sysdb_setup, + test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_get_sudo_user_info_nogroup, + test_sysdb_setup, + test_sysdb_teardown), + + cmocka_unit_test_setup_teardown(test_get_sudo_nouser, + test_sysdb_setup, + test_sysdb_teardown), + + /* The override tests use a different setup/teardown because loading + * the view allocates some data on the confdb and domain pointers, + * which would confuse the leak check + */ + cmocka_unit_test_setup_teardown(test_get_overriden_sudo_user_info, + test_sysdb_views_setup, + test_sysdb_views_teardown), + + /* sysdb_set_sudo_rule_attr() */ + cmocka_unit_test_setup_teardown(test_set_sudo_rule_attr_add, + test_sysdb_setup, + test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_set_sudo_rule_attr_replace, + test_sysdb_setup, + test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_set_sudo_rule_attr_delete, + test_sysdb_setup, + test_sysdb_teardown), + + /* sysdb_search_sudo_rules() */ + cmocka_unit_test_setup_teardown(test_search_sudo_rules, + test_sysdb_setup, + test_sysdb_teardown), + + /* sysdb_sudo_filter_rules_by_time() */ + cmocka_unit_test_setup_teardown(test_filter_rules_by_time, + test_sysdb_setup, + test_sysdb_teardown), + + /* sysdb_sudo_convert_time() */ + cmocka_unit_test(test_sudo_convert_time) + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/cmocka/test_sysdb_ts_cache.c b/src/tests/cmocka/test_sysdb_ts_cache.c new file mode 100644 index 0000000..24b26d9 --- /dev/null +++ b/src/tests/cmocka/test_sysdb_ts_cache.c @@ -0,0 +1,1798 @@ +/* + SSSD + + sysdb_ts - Test for sysdb timestamp cache + + Copyright (C) 2016 Red Hat + + 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "db/sysdb_private.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "tests_conf.ldb" +#define TEST_ID_PROVIDER "ldap" + +#define TEST_DOM1_NAME "test_sysdb_ts_1" + +#define TEST_GROUP_NAME "test_group" +#define TEST_GROUP_NAME_2 "test_group_2" +#define TEST_GROUP_NAME_3 "test_group_3" +#define TEST_GROUP_NAME_OLD "test_group_old" +#define TEST_GROUP_GID 1234 +#define TEST_GROUP_GID_2 1235 +#define TEST_GROUP_GID_3 1236 +#define TEST_GROUP_SID "S-1-5-21-123-456-789-111" + +#define TEST_USER_NAME "test_user" +#define TEST_USER_UID 4321 +#define TEST_USER_GID 4322 +#define TEST_USER_SID "S-1-5-21-123-456-789-222" +#define TEST_USER_UPN "test_user@TEST_REALM" + +#define TEST_MODSTAMP_1 "20160408132553Z" +#define TEST_MODSTAMP_2 "20160408142553Z" +#define TEST_MODSTAMP_3 "20160408152553Z" + +#define TEST_CACHE_TIMEOUT 5 + +#define TEST_NOW_1 100 +#define TEST_NOW_2 200 +#define TEST_NOW_3 300 +#define TEST_NOW_4 400 +#define TEST_NOW_5 500 +#define TEST_NOW_6 600 + +#define TS_FILTER_ALL "("SYSDB_CACHE_EXPIRE"=*)" + +struct sysdb_ts_test_ctx { + struct sss_test_ctx *tctx; +}; + +const char *domains[] = { TEST_DOM1_NAME, + NULL }; + +static int test_sysdb_ts_setup(void **state) +{ + struct sysdb_ts_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, + struct sysdb_ts_test_ctx); + assert_non_null(test_ctx); + + test_dom_suite_setup(TESTS_PATH); + + test_ctx->tctx = create_multidom_test_ctx(test_ctx, TESTS_PATH, + TEST_CONF_DB, domains, + TEST_ID_PROVIDER, NULL); + assert_non_null(test_ctx->tctx); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int test_sysdb_ts_teardown(void **state) +{ + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + + //assert_true(check_leaks_pop(test_ctx)); + talloc_zfree(test_ctx); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM1_NAME); + return 0; +} + +static struct sysdb_attrs *create_modstamp_attrs(TALLOC_CTX *mem_ctx, + const char *modstamp) +{ + int ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(mem_ctx); + if (attrs == NULL) { + return NULL; + } + + ret = sysdb_attrs_add_string(attrs, + SYSDB_ORIG_MODSTAMP, + modstamp); + if (ret != EOK) { + talloc_free(attrs); + return NULL; + } + + return attrs; +} + +static struct sysdb_attrs *create_str_attrs(TALLOC_CTX *mem_ctx, + const char *key, + const char *value) +{ + int ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(mem_ctx); + if (attrs == NULL) { + return NULL; + } + + ret = sysdb_attrs_add_string(attrs, key, value); + if (ret != EOK) { + talloc_free(attrs); + return NULL; + } + + return attrs; +} + +static struct sysdb_attrs *create_sidstr_attrs(TALLOC_CTX *mem_ctx, + const char *sid_str) +{ + return create_str_attrs(mem_ctx, SYSDB_SID_STR, sid_str); +} + +static struct sysdb_attrs *create_upnstr_attrs(TALLOC_CTX *mem_ctx, + const char *upn_str) +{ + return create_str_attrs(mem_ctx, SYSDB_UPN, upn_str); +} + +static struct sysdb_attrs *create_ts_attrs(TALLOC_CTX *mem_ctx, + time_t expiration, + time_t last_update) +{ + int ret; + struct sysdb_attrs *attrs; + + attrs = sysdb_new_attrs(mem_ctx); + if (attrs == NULL) { + return NULL; + } + + ret = sysdb_attrs_add_time_t(attrs, + SYSDB_CACHE_EXPIRE, + expiration); + if (ret != EOK) { + talloc_free(attrs); + return NULL; + } + + ret = sysdb_attrs_add_time_t(attrs, + SYSDB_LAST_UPDATE, + last_update); + if (ret != EOK) { + talloc_free(attrs); + return NULL; + } + + return attrs; +} + +static struct ldb_result *sysdb_getgrnam_res(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *name) +{ + int ret; + struct ldb_result *res = NULL; + + ret = sysdb_getgrnam(mem_ctx, domain, name, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + + return res; +} + +static struct ldb_result *sysdb_getpwnam_res(TALLOC_CTX *mem_ctx, + struct sss_domain_info *domain, + const char *name) +{ + int ret; + struct ldb_result *res = NULL; + + ret = sysdb_getpwnam(mem_ctx, domain, name, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + + return res; +} + +static uint64_t get_dn_cache_timestamp(struct sysdb_ts_test_ctx *test_ctx, + struct ldb_dn *dn) +{ + int ret; + uint64_t cache_expire_sysdb; + struct ldb_result *res; + + const char *attrs[] = { SYSDB_CACHE_EXPIRE, + NULL, + }; + + ret = ldb_search(test_ctx->tctx->sysdb->ldb, test_ctx, &res, + dn, LDB_SCOPE_BASE, attrs, NULL); + if (ret != EOK || res == NULL || res->count != 1) { + talloc_free(res); + return 0; + } + + cache_expire_sysdb = ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_CACHE_EXPIRE, + 0); + talloc_free(res); + return cache_expire_sysdb; +} + +static uint64_t get_gr_cache_timestamp(struct sysdb_ts_test_ctx *test_ctx, + const char *name) +{ + struct ldb_dn *dn; + uint64_t cache_expire_sysdb; + + dn = sysdb_group_dn(test_ctx, test_ctx->tctx->dom, name); + if (dn == NULL) { + return 0; + } + + cache_expire_sysdb = get_dn_cache_timestamp(test_ctx, dn); + talloc_free(dn); + return cache_expire_sysdb; +} + +static uint64_t get_pw_cache_timestamp(struct sysdb_ts_test_ctx *test_ctx, + const char *name) +{ + struct ldb_dn *dn; + uint64_t cache_expire_sysdb; + + dn = sysdb_user_dn(test_ctx, test_ctx->tctx->dom, name); + if (dn == NULL) { + return 0; + } + + cache_expire_sysdb = get_dn_cache_timestamp(test_ctx, dn); + talloc_free(dn); + return cache_expire_sysdb; +} + +static uint64_t get_dn_ts_cache_timestamp(struct sysdb_ts_test_ctx *test_ctx, + struct ldb_dn *dn) +{ + size_t msg_count; + struct ldb_message **msgs; + uint64_t cache_expire_ts; + const char *attrs[] = { SYSDB_CACHE_EXPIRE, + NULL, + }; + int ret; + + ret = sysdb_search_ts_entry(test_ctx, test_ctx->tctx->sysdb, + dn, LDB_SCOPE_BASE, NULL, attrs, + &msg_count, &msgs); + if (ret != EOK) { + return 0; + } + + if (msg_count != 1) { + return 0; + } + + cache_expire_ts = ldb_msg_find_attr_as_uint64(msgs[0], + SYSDB_CACHE_EXPIRE, 0); + talloc_free(msgs); + return cache_expire_ts; +} + +static uint64_t get_gr_ts_cache_timestamp(struct sysdb_ts_test_ctx *test_ctx, + const char *name) +{ + struct ldb_dn *dn; + uint64_t cache_expire_ts; + + dn = sysdb_group_dn(test_ctx, test_ctx->tctx->dom, name); + if (dn == NULL) { + return 0; + } + + cache_expire_ts = get_dn_ts_cache_timestamp(test_ctx, dn); + talloc_free(dn); + return cache_expire_ts; +} + +static uint64_t get_pw_ts_cache_timestamp(struct sysdb_ts_test_ctx *test_ctx, + const char *name) +{ + struct ldb_dn *dn; + uint64_t cache_expire_ts; + + dn = sysdb_user_dn(test_ctx, test_ctx->tctx->dom, name); + if (dn == NULL) { + return 0; + } + + cache_expire_ts = get_dn_ts_cache_timestamp(test_ctx, dn); + talloc_free(dn); + return cache_expire_ts; +} + +static void get_gr_timestamp_attrs(struct sysdb_ts_test_ctx *test_ctx, + const char *name, + uint64_t *cache_expire_sysdb, + uint64_t *cache_expire_ts) +{ + *cache_expire_sysdb = get_gr_cache_timestamp(test_ctx, name); + *cache_expire_ts = get_gr_ts_cache_timestamp(test_ctx, name); +} + +static void get_pw_timestamp_attrs(struct sysdb_ts_test_ctx *test_ctx, + const char *name, + uint64_t *cache_expire_sysdb, + uint64_t *cache_expire_ts) +{ + *cache_expire_sysdb = get_pw_cache_timestamp(test_ctx, name); + *cache_expire_ts = get_pw_ts_cache_timestamp(test_ctx, name); +} + +static void test_sysdb_group_update(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct ldb_result *res = NULL; + struct sysdb_attrs *group_attrs = NULL; + uint64_t cache_expire_sysdb; + uint64_t cache_expire_ts; + char *test_user_member = NULL; + + /* Nothing must be stored in either cache at the beginning of the test */ + res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + /* Store a group without a modifyTimestamp. Must not throw an error. This + * tests that the sysdb timestamp code is able to cope with absence of an + * attribute it operates on gracefully. + */ + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_1); + + /* Store a group and add a modifyTimestamp this time. + * Since we want to write the timestamp attributes if they are not present, + * both caches will be bumped. + */ + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(group_attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_2); + assert_int_equal(ret, EOK); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_2); + + /* Update the same attrs and the same modifyTimestamp. + * Only the timestamp cache must be bumped */ + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_3); + assert_int_equal(ret, EOK); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_3); + + /* Update with different modifyTimestamp but same attrs as previously + * saved to the timestamp cache. We should detect the 'real' attributes + * are the same and only bump the timestamp cache + */ + talloc_free(group_attrs); + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2); + assert_non_null(group_attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_4); + assert_int_equal(ret, EOK); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_4); + + /* Update with different modifyTimestamp and different attrs (add a + * member as a real-world example). Both caches must be updated. */ + ret = sysdb_store_user(test_ctx->tctx->dom, + TEST_USER_NAME, + NULL, + TEST_USER_UID, + TEST_USER_GID, + NULL, NULL, NULL, NULL, NULL, NULL, + TEST_CACHE_TIMEOUT, TEST_NOW_5); + assert_int_equal(ret, EOK); + + talloc_free(group_attrs); + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_3); + assert_non_null(group_attrs); + + test_user_member = sysdb_user_strdn(group_attrs, + test_ctx->tctx->dom->name, + TEST_USER_NAME); + assert_non_null(test_user_member); + + ret = sysdb_attrs_add_string(group_attrs, SYSDB_MEMBER, test_user_member); + assert_int_equal(ret, EOK); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_5); + assert_int_equal(ret, EOK); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_5); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_5); + + /* Try to save the same member again, while it's already saved. Only the + * timestamps cache must be bumped now + */ + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_6); + assert_int_equal(ret, EOK); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_5); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_6); + talloc_free(group_attrs); +} + +static void test_sysdb_group_delete(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct ldb_result *res = NULL; + struct sysdb_attrs *group_attrs = NULL; + uint64_t cache_expire_sysdb; + uint64_t cache_expire_ts; + struct ldb_result *ts_res; + + ts_res = talloc_zero(test_ctx, struct ldb_result); + assert_non_null(ts_res); + + /* Nothing must be stored in either cache at the beginning of the test */ + res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + ret = sysdb_search_ts_groups(ts_res, + test_ctx->tctx->dom, + TS_FILTER_ALL, + sysdb_ts_cache_attrs, + ts_res); + assert_int_equal(ret, ENOENT); + + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(group_attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + talloc_free(group_attrs); + + ret = sysdb_search_ts_groups(ts_res, + test_ctx->tctx->dom, + TS_FILTER_ALL, + sysdb_ts_cache_attrs, + ts_res); + assert_int_equal(ret, EOK); + assert_int_equal(ts_res->count, 1); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_1); + + ret = sysdb_delete_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID); + assert_int_equal(ret, EOK); + + /* Nothing must be stored in either cache at the end of the test */ + ret = sysdb_search_ts_groups(ts_res, + test_ctx->tctx->dom, + TS_FILTER_ALL, + sysdb_ts_cache_attrs, + ts_res); + assert_int_equal(ret, ENOENT); + + res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, 0); + assert_int_equal(cache_expire_ts, 0); + + talloc_free(ts_res); +} + +static void test_sysdb_group_rename(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct ldb_result *res = NULL; + uint64_t cache_expire_sysdb; + uint64_t cache_expire_ts; + struct ldb_result *ts_res; + char *filter; + + ts_res = talloc_zero(test_ctx, struct ldb_result); + assert_non_null(ts_res); + + /* Nothing must be stored in either cache at the beginning of the test */ + res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, + TEST_GROUP_NAME_OLD); + assert_int_equal(res->count, 0); + talloc_free(res); + + filter = talloc_asprintf(ts_res, "(|(%s=%s)(%s=%s))", + SYSDB_NAME, TEST_GROUP_NAME_OLD, + SYSDB_NAME, TEST_GROUP_NAME); + assert_non_null(filter); + + ret = sysdb_search_ts_groups(ts_res, + test_ctx->tctx->dom, + filter, + sysdb_ts_cache_attrs, + ts_res); + assert_int_equal(ret, ENOENT); + talloc_free(filter); + + /* Store an old group */ + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME_OLD, + TEST_GROUP_GID, + NULL, + TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME_OLD, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_1); + + /* Replace with a new one */ + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + NULL, + TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + + /* The old entry must be gone from both caches */ + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME_OLD, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, 0); + assert_int_equal(cache_expire_ts, 0); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_1); + + res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, + TEST_GROUP_NAME_OLD); + assert_int_equal(res->count, 0); + talloc_free(res); + + talloc_free(ts_res); +} + +static void assert_ts_attrs_msg(struct ldb_message *msg, + uint64_t exp_expiration, + uint64_t exp_last_update) +{ + uint64_t expiration; + uint64_t last_update; + const char *modstamp; + + /* Attributes normally requested with getgrnam are merged */ + expiration = ldb_msg_find_attr_as_uint64(msg, SYSDB_CACHE_EXPIRE, 0); + assert_int_equal(expiration, exp_expiration); + last_update = ldb_msg_find_attr_as_uint64(msg, SYSDB_LAST_UPDATE, 0); + assert_int_equal(last_update, exp_last_update); + + /* Attributes not requested are not */ + modstamp = ldb_msg_find_attr_as_string(msg, SYSDB_ORIG_MODSTAMP, NULL); + assert_null(modstamp); +} + +static void assert_ts_attrs_res(struct ldb_result *res, + uint64_t exp_expiration, + uint64_t exp_last_update) +{ + return assert_ts_attrs_msg(res->msgs[0], exp_expiration, exp_last_update); +} + +static void assert_ts_attrs_msgs_list(size_t msgs_count, + struct ldb_message **msgs, + uint64_t exp_expiration, + uint64_t exp_last_update) +{ + struct ldb_result res; + + res.count = msgs_count; + res.msgs = msgs; + return assert_ts_attrs_res(&res, exp_expiration, exp_last_update); +} + +static void test_sysdb_getgr_merges(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct sysdb_attrs *group_attrs = NULL; + const char *gr_fetch_attrs[] = SYSDB_GRSRC_ATTRS; + char *filter = NULL; + struct ldb_result *res = NULL; + size_t msgs_count; + struct ldb_message **msgs = NULL; + struct ldb_message *msg = NULL; + + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(group_attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_1); + talloc_free(group_attrs); + assert_int_equal(ret, EOK); + + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2); + assert_non_null(group_attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_2); + talloc_free(group_attrs); + assert_int_equal(ret, EOK); + + ret = sysdb_getgrnam(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(res); + + ret = sysdb_getgrgid(test_ctx, test_ctx->tctx->dom, TEST_GROUP_GID, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(res); + + filter = talloc_asprintf(test_ctx, "(%s=%s)", + SYSDB_NAME, TEST_GROUP_NAME); + assert_non_null(filter); + ret = sysdb_search_groups(test_ctx, test_ctx->tctx->dom, + filter, gr_fetch_attrs, + &msgs_count, &msgs); + talloc_free(filter); + assert_int_equal(ret, EOK); + assert_int_equal(msgs_count, 1); + assert_ts_attrs_msgs_list(msgs_count, msgs, + TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(msgs); + + group_attrs = create_ts_attrs(test_ctx, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + assert_non_null(group_attrs); + ret = sysdb_set_group_attr(test_ctx->tctx->dom, TEST_GROUP_NAME, + group_attrs, SYSDB_MOD_REP); + talloc_free(group_attrs); + + ret = sysdb_getgrnam(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + talloc_free(res); + + /* Make sure sysdb_search_group_by_name includes timestamp attributes */ + ret = sysdb_search_group_by_name(test_ctx, test_ctx->tctx->dom, + TEST_GROUP_NAME, gr_fetch_attrs, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + assert_ts_attrs_msg(msg, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + talloc_free(msg); + + ret = sysdb_search_group_by_gid(test_ctx, test_ctx->tctx->dom, + TEST_GROUP_GID, gr_fetch_attrs, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + assert_ts_attrs_msg(msg, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + talloc_free(msg); +} + +static void test_merge_ldb_results(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + const char *gr_fetch_attrs[] = SYSDB_GRSRC_ATTRS; + char *filter; + struct ldb_result *res; + struct ldb_result *res1; + struct ldb_result *res2; + size_t msgs_count; + + res1 = talloc_zero(test_ctx, struct ldb_result); + assert_non_null(res1); + res2 = talloc_zero(test_ctx, struct ldb_result); + assert_non_null(res2); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + NULL, + TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME_2, + TEST_GROUP_GID_2, + NULL, + TEST_CACHE_TIMEOUT, + TEST_NOW_2); + assert_int_equal(ret, EOK); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME_3, + TEST_GROUP_GID_3, + NULL, + TEST_CACHE_TIMEOUT, + TEST_NOW_3); + assert_int_equal(ret, EOK); + + filter = talloc_asprintf(test_ctx, "(|(%s=%s)(%s=%s))", + SYSDB_NAME, TEST_GROUP_NAME, + SYSDB_NAME, TEST_GROUP_NAME_2); + assert_non_null(filter); + ret = sysdb_search_groups(res1, test_ctx->tctx->dom, + filter, gr_fetch_attrs, + &msgs_count, &res1->msgs); + res1->count = (unsigned)msgs_count; + talloc_free(filter); + assert_int_equal(ret, EOK); + assert_int_equal(res1->count, 2); + + filter = talloc_asprintf(test_ctx, "(|(%s=%s)(%s=%s))", + SYSDB_NAME, TEST_GROUP_NAME_2, + SYSDB_NAME, TEST_GROUP_NAME_3); + assert_non_null(filter); + ret = sysdb_search_groups(res2, test_ctx->tctx->dom, + filter, gr_fetch_attrs, + &msgs_count, &res2->msgs); + res2->count = (unsigned)msgs_count; + talloc_free(filter); + assert_int_equal(ret, EOK); + assert_int_equal(res2->count, 2); + + res = sss_merge_ldb_results(res1, res2); + assert_non_null(res); + assert_int_equal(res->count, 3); + + talloc_free(res1); + talloc_free(res2); +} + +static void test_group_bysid(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + const char *gr_fetch_attrs[] = SYSDB_GRSRC_ATTRS; + struct sysdb_attrs *group_attrs = NULL; + struct ldb_result *res; + struct ldb_message *msg = NULL; + struct ldb_result ts_res; + + group_attrs = create_sidstr_attrs(test_ctx, TEST_GROUP_SID); + assert_non_null(group_attrs); + + ret = sysdb_search_object_by_sid(test_ctx, + test_ctx->tctx->dom, + TEST_GROUP_SID, + gr_fetch_attrs, + &res); + assert_int_equal(ret, ENOENT); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_1); + talloc_free(group_attrs); + assert_int_equal(ret, EOK); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + NULL, + TEST_CACHE_TIMEOUT, + TEST_NOW_2); + assert_int_equal(ret, EOK); + + ret = sysdb_search_object_by_sid(test_ctx, + test_ctx->tctx->dom, + TEST_GROUP_SID, + gr_fetch_attrs, + &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(res); + + ret = sysdb_search_group_by_sid_str(test_ctx, + test_ctx->tctx->dom, + TEST_GROUP_SID, + gr_fetch_attrs, + &msg); + assert_int_equal(ret, EOK); + assert_ts_attrs_msg(msg, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + + ret = sysdb_delete_by_sid(test_ctx->tctx->dom->sysdb, + test_ctx->tctx->dom, + TEST_GROUP_SID); + assert_int_equal(ret, EOK); + + ret = sysdb_search_object_by_sid(test_ctx, + test_ctx->tctx->dom, + TEST_GROUP_SID, + gr_fetch_attrs, + &res); + assert_int_equal(ret, ENOENT); + + ret = sysdb_search_ts_groups(test_ctx, + test_ctx->tctx->dom, + TS_FILTER_ALL, + sysdb_ts_cache_attrs, + &ts_res); + assert_int_equal(ret, ENOENT); +} + +static void test_sysdb_user_update(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct ldb_result *res = NULL; + struct sysdb_attrs *user_attrs = NULL; + uint64_t cache_expire_sysdb; + uint64_t cache_expire_ts; + + /* Nothing must be stored in either cache at the beginning of the test */ + res = sysdb_getpwnam_res(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + /* Store a user without a modifyTimestamp. Must not throw an error. This + * tests that the sysdb timestamp code is able to cope with absence of an + * attribute it operates on gracefully. + */ + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + + get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_1); + + /* Store a user and add a modifyTimestamp this time. + * Since we want to write the timestamp attributes if they are not present, + * both caches will be bumped. + */ + user_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(user_attrs); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_2); + assert_int_equal(ret, EOK); + + get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_2); + + /* Update with different modifyTimestamp but same attrs as previously + * saved to the timestamp cache. We should detect the 'real' attributes + * are the same and only bump the timestamp cache + */ + talloc_free(user_attrs); + user_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2); + assert_non_null(user_attrs); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_4); + assert_int_equal(ret, EOK); + + get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_4); + + /* Update with different modifyTimestamp and different attrs (change + * the shell as a real-world example). Both caches must be updated. */ + talloc_free(user_attrs); + user_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_3); + assert_non_null(user_attrs); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/zsh", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_5); + assert_int_equal(ret, EOK); + + get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_5); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_5); +} + +static void test_sysdb_user_delete(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct ldb_result *res = NULL; + struct sysdb_attrs *user_attrs = NULL; + uint64_t cache_expire_sysdb; + uint64_t cache_expire_ts; + struct ldb_result *ts_res; + + ts_res = talloc_zero(test_ctx, struct ldb_result); + assert_non_null(ts_res); + + /* Nothing must be stored in either cache at the beginning of the test */ + res = sysdb_getpwnam_res(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + ret = sysdb_search_ts_users(ts_res, + test_ctx->tctx->dom, + TS_FILTER_ALL, + sysdb_ts_cache_attrs, + ts_res); + assert_int_equal(ret, ENOENT); + + user_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(user_attrs); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + + get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_1); + + ret = sysdb_search_ts_users(ts_res, + test_ctx->tctx->dom, + TS_FILTER_ALL, + sysdb_ts_cache_attrs, + ts_res); + assert_int_equal(ret, EOK); + assert_int_equal(ts_res->count, 1); + + get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_1); + + ret = sysdb_delete_user(test_ctx->tctx->dom, + TEST_USER_NAME, + TEST_USER_UID); + assert_int_equal(ret, EOK); + + /* Nothing must be stored in either cache at the end of the test */ + res = sysdb_getpwnam_res(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + ret = sysdb_search_ts_users(ts_res, + test_ctx->tctx->dom, + TS_FILTER_ALL, + sysdb_ts_cache_attrs, + ts_res); + assert_int_equal(ret, ENOENT); + + get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, 0); + assert_int_equal(cache_expire_ts, 0); + + talloc_free(ts_res); +} + +static void test_sysdb_getpw_merges(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct sysdb_attrs *user_attrs = NULL; + const char *pw_fetch_attrs[] = SYSDB_PW_ATTRS; + char *filter = NULL; + struct ldb_result *res = NULL; + size_t msgs_count; + struct ldb_message **msgs = NULL; + struct ldb_message *msg = NULL; + + user_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(user_attrs); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_1); + talloc_free(user_attrs); + assert_int_equal(ret, EOK); + + user_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2); + assert_non_null(user_attrs); + + /* sysdb cache will have test_now1 and ts cache test_now2 at this point */ + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_2); + talloc_free(user_attrs); + assert_int_equal(ret, EOK); + + /* getpwnam must return the timestamp from the ts cache */ + ret = sysdb_getpwnam(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(res); + + /* getpwuid must return the timestamp from the ts cache */ + ret = sysdb_getpwuid(test_ctx, test_ctx->tctx->dom, TEST_USER_UID, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(res); + + filter = talloc_asprintf(test_ctx, "(%s=%s)", + SYSDB_NAME, TEST_USER_NAME); + assert_non_null(filter); + ret = sysdb_search_users(test_ctx, test_ctx->tctx->dom, + filter, pw_fetch_attrs, + &msgs_count, &msgs); + talloc_free(filter); + assert_int_equal(ret, EOK); + assert_int_equal(msgs_count, 1); + assert_ts_attrs_msgs_list(msgs_count, msgs, + TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(msgs); + + /* set_user_attrs must bump the ts cache */ + user_attrs = create_ts_attrs(test_ctx, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + assert_non_null(user_attrs); + ret = sysdb_set_user_attr(test_ctx->tctx->dom, TEST_USER_NAME, + user_attrs, SYSDB_MOD_REP); + talloc_free(user_attrs); + + /* getpwnam must return the timestamp from the ts cache */ + ret = sysdb_getpwnam(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + talloc_free(res); + + ret = sysdb_initgroups(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + talloc_free(res); + + ret = sysdb_get_user_attr(test_ctx, test_ctx->tctx->dom, + TEST_USER_NAME, pw_fetch_attrs, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + talloc_free(res); + + /* Make sure sysdb_search_user_by_name includes timestamp attributes */ + ret = sysdb_search_user_by_name(test_ctx, test_ctx->tctx->dom, + TEST_USER_NAME, pw_fetch_attrs, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + assert_ts_attrs_msg(msg, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + talloc_free(msg); + + ret = sysdb_search_user_by_uid(test_ctx, test_ctx->tctx->dom, + TEST_USER_UID, pw_fetch_attrs, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + assert_ts_attrs_msg(msg, TEST_NOW_3 + TEST_CACHE_TIMEOUT, TEST_NOW_3); + talloc_free(msg); +} + +static void test_user_bysid(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + const char *pw_fetch_attrs[] = SYSDB_PW_ATTRS; + struct sysdb_attrs *user_attrs = NULL; + struct ldb_result *res; + struct ldb_message *msg = NULL; + struct ldb_result ts_res; + + user_attrs = create_sidstr_attrs(test_ctx, TEST_USER_SID); + assert_non_null(user_attrs); + + ret = sysdb_search_object_by_sid(test_ctx, + test_ctx->tctx->dom, + TEST_USER_SID, + pw_fetch_attrs, + &res); + assert_int_equal(ret, ENOENT); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_1); + talloc_zfree(user_attrs); + assert_int_equal(ret, EOK); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_2); + assert_int_equal(ret, EOK); + + ret = sysdb_search_object_by_sid(test_ctx, + test_ctx->tctx->dom, + TEST_USER_SID, + pw_fetch_attrs, + &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(res); + + ret = sysdb_search_user_by_sid_str(test_ctx, + test_ctx->tctx->dom, + TEST_USER_SID, + pw_fetch_attrs, + &msg); + assert_int_equal(ret, EOK); + assert_ts_attrs_msg(msg, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + + ret = sysdb_delete_by_sid(test_ctx->tctx->dom->sysdb, + test_ctx->tctx->dom, + TEST_USER_SID); + assert_int_equal(ret, EOK); + + ret = sysdb_search_object_by_sid(test_ctx, + test_ctx->tctx->dom, + TEST_USER_SID, + pw_fetch_attrs, + &res); + assert_int_equal(ret, ENOENT); + + ret = sysdb_search_ts_users(test_ctx, + test_ctx->tctx->dom, + TS_FILTER_ALL, + sysdb_ts_cache_attrs, + &ts_res); + assert_int_equal(ret, ENOENT); +} + +static void test_user_byupn(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + const char *pw_fetch_attrs[] = SYSDB_PW_ATTRS; + struct sysdb_attrs *user_attrs = NULL; + struct ldb_result *res; + struct ldb_message *msg = NULL; + + user_attrs = create_upnstr_attrs(test_ctx, TEST_USER_UPN); + assert_non_null(user_attrs); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_1); + talloc_zfree(user_attrs); + assert_int_equal(ret, EOK); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + user_attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_2); + assert_int_equal(ret, EOK); + + ret = sysdb_getpwupn(test_ctx, test_ctx->tctx->dom, false, TEST_USER_UPN, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(res); + + ret = sysdb_search_user_by_upn_res(test_ctx, test_ctx->tctx->dom, + false, TEST_USER_UPN, pw_fetch_attrs, + &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(res); + + ret = sysdb_search_user_by_upn(test_ctx, test_ctx->tctx->dom, + false, TEST_USER_UPN, pw_fetch_attrs, + &msg); + assert_int_equal(ret, EOK); + assert_ts_attrs_msg(msg, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + + ret = sysdb_initgroups_by_upn(test_ctx, test_ctx->tctx->dom, + TEST_USER_UPN, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_ts_attrs_res(res, TEST_NOW_2 + TEST_CACHE_TIMEOUT, TEST_NOW_2); + talloc_free(res); +} + +static void test_sysdb_zero_now(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct ldb_result *res = NULL; + uint64_t cache_expire_sysdb; + uint64_t cache_expire_ts; + struct sysdb_attrs *attrs = NULL; + + /* Nothing must be stored in either cache at the beginning of the test */ + res = sysdb_getpwnam_res(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(attrs); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + attrs, NULL, TEST_CACHE_TIMEOUT, + 0); + talloc_zfree(attrs); + assert_int_equal(ret, EOK); + + attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + attrs, + TEST_CACHE_TIMEOUT, + 0); + talloc_zfree(attrs); + assert_int_equal(ret, EOK); + talloc_zfree(attrs); + + attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(attrs); + + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + attrs, NULL, TEST_CACHE_TIMEOUT, + 0); + talloc_zfree(attrs); + assert_int_equal(ret, EOK); + + attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + attrs, + TEST_CACHE_TIMEOUT, + 0); + talloc_zfree(attrs); + assert_int_equal(ret, EOK); + + /* Even though we passed zero as the timestamp, the timestamp cache should + * have used the current time instead + */ + get_pw_timestamp_attrs(test_ctx, TEST_USER_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_true(cache_expire_sysdb > TEST_CACHE_TIMEOUT); + assert_true(cache_expire_ts > TEST_CACHE_TIMEOUT); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_true(cache_expire_sysdb > TEST_CACHE_TIMEOUT); + assert_true(cache_expire_ts > TEST_CACHE_TIMEOUT); +} + +static void test_sysdb_search_with_ts(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct ldb_result *res = NULL; + struct ldb_dn *base_dn; + const char *attrs[] = { SYSDB_NAME, + SYSDB_OBJECTCATEGORY, + SYSDB_GIDNUM, + SYSDB_CACHE_EXPIRE, + NULL }; + struct sysdb_attrs *group_attrs = NULL; + char *filter; + uint64_t cache_expire_sysdb; + uint64_t cache_expire_ts; + size_t count; + struct ldb_message **msgs; + + base_dn = sysdb_base_dn(test_ctx->tctx->dom->sysdb, test_ctx); + assert_non_null(base_dn); + + /* Nothing must be stored in either cache at the beginning of the test */ + ret = sysdb_search_with_ts_attr(test_ctx, + test_ctx->tctx->dom, + base_dn, + LDB_SCOPE_SUBTREE, + SYSDB_CACHE_TYPE_NONE, + SYSDB_NAME"=*", + attrs, + &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + talloc_free(res); + + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(group_attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + talloc_zfree(group_attrs); + + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(group_attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME_2, + TEST_GROUP_GID_2, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_2); + assert_int_equal(ret, EOK); + talloc_zfree(group_attrs); + + /* Bump the timestamps in the cache so that the ts cache + * and sysdb differ + */ + + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(group_attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_3); + assert_int_equal(ret, EOK); + + talloc_zfree(group_attrs); + + + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(group_attrs); + + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME_2, + TEST_GROUP_GID_2, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_4); + assert_int_equal(ret, EOK); + + talloc_zfree(group_attrs); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_3); + + get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME_2, + &cache_expire_sysdb, &cache_expire_ts); + assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2); + assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_4); + + /* Search for groups that don't expire until TEST_NOW_4 */ + filter = talloc_asprintf(test_ctx, SYSDB_CACHE_EXPIRE">=%d", TEST_NOW_4); + assert_non_null(filter); + + /* This search should yield only one group (so, it needs to search the ts + * cache to hit the TEST_NOW_4), but should return attributes merged from + * both caches + */ + ret = sysdb_search_with_ts_attr(test_ctx, + test_ctx->tctx->dom, + base_dn, + LDB_SCOPE_SUBTREE, + SYSDB_CACHE_TYPE_NONE, + filter, + attrs, + &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_int_equal(TEST_GROUP_GID_2, ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_GIDNUM, 0)); + talloc_free(res); + + /* + * In contrast, sysdb_search_entry merges the timestamp attributes, but does + * not search the timestamp cache + */ + ret = sysdb_search_entry(test_ctx, + test_ctx->tctx->dom->sysdb, + base_dn, + LDB_SCOPE_SUBTREE, + filter, + attrs, + &count, + &msgs); + assert_int_equal(ret, ENOENT); + + /* Should get the same result when searching by ts attrs only */ + ret = sysdb_search_with_ts_attr(test_ctx, + test_ctx->tctx->dom, + base_dn, + LDB_SCOPE_SUBTREE, + SYSDB_CACHE_TYPE_TIMESTAMP, + filter, + attrs, + &res); + talloc_zfree(filter); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_int_equal(TEST_GROUP_GID_2, ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_GIDNUM, 0)); + talloc_free(res); + + /* We can also search in sysdb only as well, we should get back ts attrs */ + filter = talloc_asprintf(test_ctx, SYSDB_GIDNUM"=%d", TEST_GROUP_GID); + assert_non_null(filter); + + ret = sysdb_search_with_ts_attr(test_ctx, + test_ctx->tctx->dom, + base_dn, + LDB_SCOPE_SUBTREE, + SYSDB_CACHE_TYPE_PERSISTENT, + filter, + attrs, + &res); + talloc_zfree(filter); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_int_equal(TEST_GROUP_GID, ldb_msg_find_attr_as_uint64(res->msgs[0], + SYSDB_GIDNUM, 0)); + assert_int_equal(TEST_CACHE_TIMEOUT + TEST_NOW_3, + ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_CACHE_EXPIRE, 0)); + talloc_free(res); + + /* We can also search in both using an OR-filter. Note that an AND-filter is not possible + * unless we deconstruct the filter.. + */ + filter = talloc_asprintf(test_ctx, "(|("SYSDB_GIDNUM"=%d)" + "("SYSDB_CACHE_EXPIRE">=%d))", + TEST_GROUP_GID, TEST_NOW_4); + assert_non_null(filter); + + ret = sysdb_search_with_ts_attr(test_ctx, + test_ctx->tctx->dom, + base_dn, + LDB_SCOPE_SUBTREE, + SYSDB_CACHE_TYPE_NONE, + filter, + attrs, + &res); + talloc_zfree(filter); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 2); + talloc_free(res); +} + +static void test_sysdb_user_missing_ts(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct ldb_result *res = NULL; + struct sysdb_attrs *attrs = NULL; + + /* Nothing must be stored in either cache at the beginning of the test */ + res = sysdb_getpwnam_res(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + /* add user to cache */ + attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(attrs); + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + talloc_zfree(attrs); + + /* remove timestamp */ + struct ldb_dn *userdn = sysdb_user_dn(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME); + ret = ldb_delete(test_ctx->tctx->dom->sysdb->ldb_ts, userdn); + assert_int_equal(ret, EOK); + + /* update user */ + attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2); + assert_non_null(attrs); + ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, + "/home/"TEST_USER_NAME, "/bin/bash", NULL, + attrs, NULL, TEST_CACHE_TIMEOUT, + TEST_NOW_2); + assert_int_equal(ret, EOK); + talloc_zfree(attrs); + + /* check that ts is back */ + SSS_LDB_SEARCH(ret, test_ctx->tctx->dom->sysdb->ldb_ts, test_ctx, &res, userdn, + LDB_SCOPE_BASE, NULL, NULL); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + talloc_zfree(res); + talloc_zfree(userdn); +} + +static void test_sysdb_group_missing_ts(void **state) +{ + int ret; + struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_ts_test_ctx); + struct ldb_result *res = NULL; + struct sysdb_attrs *group_attrs = NULL; + struct ldb_dn *groupdn = NULL; + + /* Nothing must be stored in either cache at the beginning of the test */ + res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME); + assert_int_equal(res->count, 0); + talloc_free(res); + + /* add group to cache */ + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); + assert_non_null(group_attrs); + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_1); + assert_int_equal(ret, EOK); + talloc_zfree(group_attrs); + + /* remove timestamp */ + groupdn = sysdb_group_dn(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME); + ret = ldb_delete(test_ctx->tctx->dom->sysdb->ldb_ts, groupdn); + assert_int_equal(ret, EOK); + + /* update group */ + group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2); + assert_non_null(group_attrs); + ret = sysdb_store_group(test_ctx->tctx->dom, + TEST_GROUP_NAME, + TEST_GROUP_GID, + group_attrs, + TEST_CACHE_TIMEOUT, + TEST_NOW_2); + assert_int_equal(ret, EOK); + talloc_zfree(group_attrs); + + /* check that ts is back */ + SSS_LDB_SEARCH(ret, test_ctx->tctx->dom->sysdb->ldb_ts, test_ctx, &res, groupdn, + LDB_SCOPE_BASE, NULL, NULL); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + talloc_zfree(res); + talloc_zfree(groupdn); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sysdb_group_update, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_group_delete, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_group_rename, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_getgr_merges, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_group_bysid, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_merge_ldb_results, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_user_update, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_user_delete, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_getpw_merges, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_user_bysid, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_user_byupn, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_zero_now, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_search_with_ts, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_user_missing_ts, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_group_missing_ts, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); + test_dom_suite_setup(TESTS_PATH); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + if (rv == 0 && no_cleanup == 0) { + test_multidom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, domains); + } + return rv; +} diff --git a/src/tests/cmocka/test_sysdb_utils.c b/src/tests/cmocka/test_sysdb_utils.c new file mode 100644 index 0000000..b522568 --- /dev/null +++ b/src/tests/cmocka/test_sysdb_utils.c @@ -0,0 +1,197 @@ +/* + SSSD + + sysdb_utils - Tests for various sysdb calls + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2015 Red Hat + + 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" + +#define IPA_UUID "bcae7c40-97eb-11e4-88ca-525400e96a6b" + +#define AD_GUID_BIN {0x8d, 0x0d, 0xa8, 0xfe, 0xd5, 0xdb, 0x84, 0x4f, \ + 0x85, 0x74, 0x7d, 0xb0, 0x47, 0x7f, 0x96, 0x2e}; +#define AD_GUID "fea80d8d-dbd5-4f84-8574-7db0477f962e" +static void test_sysdb_handle_original_uuid(void **state) +{ + int ret; + struct sysdb_attrs *src_attrs; + struct sysdb_attrs *dest_attrs; + const char *guid; + uint8_t bin_guid[] = AD_GUID_BIN; + struct ldb_val guid_val = {bin_guid, 16}; + + ret = sysdb_handle_original_uuid(NULL, NULL, NULL, NULL, NULL); + assert_int_equal(ret, ENOENT); + + src_attrs = sysdb_new_attrs(NULL); + assert_non_null(src_attrs); + + dest_attrs = sysdb_new_attrs(NULL); + assert_non_null(dest_attrs); + + ret = sysdb_handle_original_uuid("xyz", src_attrs, "abc", dest_attrs, + "def"); + assert_int_equal(ret, ENOENT); + + ret = sysdb_attrs_add_val(src_attrs, "GUID", &guid_val); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_string(src_attrs, "UUID", IPA_UUID); + assert_int_equal(ret, EOK); + + ret = sysdb_handle_original_uuid(NULL, src_attrs, "GUID", + dest_attrs, "def"); + assert_int_equal(ret, ENOENT); + + ret = sysdb_handle_original_uuid("objectGUID", NULL, "GUID", + dest_attrs, "def"); + assert_int_equal(ret, EINVAL); + + ret = sysdb_handle_original_uuid("objectGUID", src_attrs, "GUID", + dest_attrs, "def"); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_get_string(dest_attrs, "def", &guid); + assert_int_equal(ret, EOK); + assert_string_equal(guid, AD_GUID); + + ret = sysdb_handle_original_uuid("ipaUniqueID", src_attrs, "UUID", + dest_attrs, "ghi"); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_get_string(dest_attrs, "ghi", &guid); + assert_int_equal(ret, EOK); + assert_string_equal(guid, IPA_UUID); + + talloc_free(src_attrs); + src_attrs = sysdb_new_attrs(NULL); + assert_non_null(src_attrs); + + /* check objectGUID with length other than 16 */ + ret = sysdb_attrs_add_string(src_attrs, "GUID", IPA_UUID); + assert_int_equal(ret, EOK); + ret = sysdb_handle_original_uuid("objectGUID", src_attrs, "GUID", + dest_attrs, "jkl"); + assert_int_equal(ret, EOK); + ret = sysdb_attrs_get_string(dest_attrs, "jkl", &guid); + assert_int_equal(ret, EOK); + assert_string_equal(guid, IPA_UUID); + + talloc_free(src_attrs); + talloc_free(dest_attrs); +} + +#define TEST_BASE64_ABC "YWJj" +#define TEST_BASE64_123 "AQID" +static void test_sysdb_attrs_add_base64_blob(void **state) +{ + struct sysdb_attrs *attrs; + struct ldb_message_element *el; + char zero[] = { '\1', '\2', '\3' }; + int ret; + + attrs = sysdb_new_attrs(NULL); + assert_non_null(attrs); + + ret = sysdb_attrs_add_base64_blob(attrs, "testAttrABC", TEST_BASE64_ABC); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_base64_blob(attrs, "testAttr000", TEST_BASE64_123); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_get_el(attrs, "testAttrABC", &el); + assert_int_equal(ret, EOK); + assert_int_equal(el->num_values, 1); + assert_non_null(el->values); + assert_non_null(el->values[0].data); + assert_int_equal(el->values[0].length, 3); + assert_memory_equal(el->values[0].data, "abc", 3); + + ret = sysdb_attrs_get_el(attrs, "testAttr000", &el); + assert_int_equal(ret, EOK); + assert_int_equal(el->num_values, 1); + assert_non_null(el->values); + assert_non_null(el->values[0].data); + assert_int_equal(el->values[0].length, 3); + assert_memory_equal(el->values[0].data, zero, 3); +} + +void test_sysdb_cert_derb64_to_ldap_filter(void **state) +{ + int ret; + char *filter; + + ret = sysdb_cert_derb64_to_ldap_filter(NULL, NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = sysdb_cert_derb64_to_ldap_filter(NULL, "AAECAwQFBgcICQ==", "attrName", + &filter); + assert_int_equal(ret, EOK); + assert_string_equal(filter, + "(attrName=\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09)"); + + talloc_free(filter); +} + + +int main(int argc, const char *argv[]) +{ + int rv; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sysdb_handle_original_uuid), + cmocka_unit_test(test_sysdb_attrs_add_base64_blob), + cmocka_unit_test(test_sysdb_cert_derb64_to_ldap_filter), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/cmocka/test_sysdb_views.c b/src/tests/cmocka/test_sysdb_views.c new file mode 100644 index 0000000..d3a6ed5 --- /dev/null +++ b/src/tests/cmocka/test_sysdb_views.c @@ -0,0 +1,1149 @@ +/* + SSSD + + sysdb_views - Tests for view and override related sysdb calls + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 <stdlib.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "providers/ipa/ipa_id.h" +#include "db/sysdb.h" +#include "db/sysdb_private.h" /* for sysdb->ldb member */ + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_FILE "tests_conf.ldb" + +#define TEST_ANCHOR_PREFIX ":ANCHOR:" +#define TEST_VIEW_NAME "test view" +#define TEST_VIEW_CONTAINER "cn=" TEST_VIEW_NAME ",cn=views,cn=sysdb" +#define TEST_USER_NAME "test_user" +#define TEST_USER_UID 1234 +#define TEST_USER_GID 5678 +#define TEST_USER_GECOS "Gecos field" +#define TEST_USER_HOMEDIR "/home/home" +#define TEST_USER_SHELL "/bin/shell" +#define TEST_USER_SID "S-1-2-3-4" +#define TEST_GID_OVERRIDE_BASE 100 + +struct sysdb_test_ctx { + struct sysdb_ctx *sysdb; + struct confdb_ctx *confdb; + struct tevent_context *ev; + struct sss_domain_info *domain; +}; + +static int _setup_sysdb_tests(struct sysdb_test_ctx **ctx, bool enumerate) +{ + struct sysdb_test_ctx *test_ctx; + char *conf_db; + int ret; + + const char *val[2]; + val[1] = NULL; + + /* Create tests directory if it doesn't exist */ + /* (relative to current dir) */ + ret = mkdir(TESTS_PATH, 0775); + assert_true(ret == 0 || errno == EEXIST); + + test_ctx = talloc_zero(global_talloc_context, struct sysdb_test_ctx); + assert_non_null(test_ctx); + + /* Create an event context + * It will not be used except in confdb_init and sysdb_init + */ + test_ctx->ev = tevent_context_init(test_ctx); + assert_non_null(test_ctx->ev); + + conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_FILE); + assert_non_null(conf_db); + DEBUG(SSSDBG_MINOR_FAILURE, "CONFDB: %s\n", conf_db); + + /* Connect to the conf db */ + ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db); + assert_int_equal(ret, EOK); + + val[0] = "FILES"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/sssd", "domains", val); + assert_int_equal(ret, EOK); + + val[0] = "proxy"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/FILES", "id_provider", val); + assert_int_equal(ret, EOK); + + val[0] = enumerate ? "TRUE" : "FALSE"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/FILES", "enumerate", val); + assert_int_equal(ret, EOK); + + val[0] = "TRUE"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/FILES", "cache_credentials", val); + assert_int_equal(ret, EOK); + + ret = sssd_domain_init(test_ctx, test_ctx->confdb, "FILES", + TESTS_PATH, &test_ctx->domain); + assert_int_equal(ret, EOK); + + test_ctx->domain->has_views = true; + test_ctx->sysdb = test_ctx->domain->sysdb; + + *ctx = test_ctx; + return EOK; +} + +#define setup_sysdb_tests(ctx) _setup_sysdb_tests((ctx), false) +#define setup_sysdb_enum_tests(ctx) _setup_sysdb_tests((ctx), true) + +static int test_sysdb_setup(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + ret = setup_sysdb_tests(&test_ctx); + assert_int_equal(ret, EOK); + + *state = (void *) test_ctx; + return 0; +} + +static int test_sysdb_teardown(void **state) +{ + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_sysdb_store_override(void **state) +{ + int ret; + struct ldb_message *msg; + struct ldb_message **msgs; + struct sysdb_attrs *attrs; + size_t count; + char *name; + const char override_dn_str[] = SYSDB_OVERRIDE_ANCHOR_UUID "=" \ + TEST_ANCHOR_PREFIX TEST_USER_SID "," TEST_VIEW_CONTAINER; + + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + test_ctx->domain->mpg_mode = MPG_DISABLED; + name = sss_create_internal_fqname(test_ctx, TEST_USER_NAME, + test_ctx->domain->name); + assert_non_null(name); + + ret = sysdb_store_user(test_ctx->domain, name, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_GECOS, + TEST_USER_HOMEDIR, TEST_USER_SHELL, NULL, NULL, NULL, + 0,0); + assert_int_equal(ret, EOK); + + ret = sysdb_search_user_by_name(test_ctx, test_ctx->domain, name, + NULL, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + + /* No override exists */ + ret = sysdb_store_override(test_ctx->domain, TEST_VIEW_NAME, + SYSDB_MEMBER_USER, NULL, msg->dn); + assert_int_equal(ret, EOK); + + ret = sysdb_search_entry(test_ctx, test_ctx->domain->sysdb,msg->dn, + LDB_SCOPE_BASE, NULL, NULL, &count, &msgs); + assert_int_equal(ret, EOK); + assert_int_equal(count, 1); + assert_string_equal(ldb_dn_get_linearized(msg->dn), + ldb_msg_find_attr_as_string(msgs[0], + SYSDB_OVERRIDE_DN, NULL)); + + ret = sysdb_invalidate_overrides(test_ctx->domain->sysdb); + assert_int_equal(ret, EOK); + + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + /* Missing anchor attribute */ + ret = sysdb_store_override(test_ctx->domain, TEST_VIEW_NAME, + SYSDB_MEMBER_USER, attrs, msg->dn); + assert_int_equal(ret, EINVAL); + + /* With anchor */ + ret = sysdb_attrs_add_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID, + TEST_ANCHOR_PREFIX TEST_USER_SID); + assert_int_equal(ret, EOK); + + ret = sysdb_store_override(test_ctx->domain, TEST_VIEW_NAME, + SYSDB_MEMBER_USER, attrs, msg->dn); + assert_int_equal(ret, EOK); + + ret = sysdb_search_entry(test_ctx, test_ctx->domain->sysdb,msg->dn, + LDB_SCOPE_BASE, NULL, NULL, &count, &msgs); + assert_int_equal(ret, EOK); + assert_int_equal(count, 1); + assert_string_equal(override_dn_str, ldb_msg_find_attr_as_string(msgs[0], + SYSDB_OVERRIDE_DN, NULL)); + +} + +void test_sysdb_add_overrides_to_object(void **state) +{ + int ret; + struct ldb_message *orig; + struct ldb_message *override; + struct ldb_message_element *el; + char *tmp_str; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + orig = ldb_msg_new(test_ctx); + assert_non_null(orig); + + tmp_str = talloc_strdup(orig, "ORIGNAME"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(orig, SYSDB_NAME, tmp_str); + assert_int_equal(ret, EOK); + + tmp_str = talloc_strdup(orig, "ORIGGECOS"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(orig, SYSDB_GECOS, tmp_str); + assert_int_equal(ret, EOK); + + override = ldb_msg_new(test_ctx); + assert_non_null(override); + + tmp_str = talloc_strdup(override, "OVERRIDENAME"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(override, SYSDB_NAME, tmp_str); + assert_int_equal(ret, EOK); + + tmp_str = talloc_strdup(override, "OVERRIDEGECOS"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(override, SYSDB_GECOS, tmp_str); + assert_int_equal(ret, EOK); + + tmp_str = talloc_strdup(override, "OVERRIDEKEY1"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(override, SYSDB_SSH_PUBKEY, tmp_str); + assert_int_equal(ret, EOK); + + tmp_str = talloc_strdup(override, "OVERRIDEKEY2"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(override, SYSDB_SSH_PUBKEY, tmp_str); + assert_int_equal(ret, EOK); + + + ret = sysdb_add_overrides_to_object(test_ctx->domain, orig, override, NULL); + assert_int_equal(ret, EOK); + + assert_string_equal(ldb_msg_find_attr_as_string(orig, SYSDB_NAME, NULL), + "ORIGNAME"); + assert_string_equal(ldb_msg_find_attr_as_string(orig, SYSDB_GECOS, NULL), + "ORIGGECOS"); + assert_string_equal(ldb_msg_find_attr_as_string(orig, + OVERRIDE_PREFIX SYSDB_NAME, + NULL), + "OVERRIDENAME"); + assert_string_equal(ldb_msg_find_attr_as_string(orig, + OVERRIDE_PREFIX SYSDB_GECOS, + NULL), + "OVERRIDEGECOS"); + + el = ldb_msg_find_element(orig, OVERRIDE_PREFIX SYSDB_SSH_PUBKEY); + assert_non_null(el); + assert_int_equal(el->num_values, 2); + assert_int_equal(ldb_val_string_cmp(&el->values[0], "OVERRIDEKEY1"), 0); + assert_int_equal(ldb_val_string_cmp(&el->values[1], "OVERRIDEKEY2"), 0); +} + +void test_sysdb_add_overrides_to_object_local(void **state) +{ + int ret; + struct ldb_message *orig; + char *tmp_str; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + orig = ldb_msg_new(test_ctx); + assert_non_null(orig); + + tmp_str = talloc_strdup(orig, "ORIGNAME"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(orig, SYSDB_NAME, tmp_str); + assert_int_equal(ret, EOK); + + tmp_str = talloc_strdup(orig, "ORIGGECOS"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(orig, SYSDB_GECOS, tmp_str); + assert_int_equal(ret, EOK); + + test_ctx->domain->has_views = true; + test_ctx->domain->view_name = "LOCAL"; + + ret = sysdb_add_overrides_to_object(test_ctx->domain, orig, NULL, NULL); + assert_int_equal(ret, EOK); +} + +void test_sysdb_add_overrides_to_object_missing_overridedn(void **state) +{ + int ret; + struct ldb_message *orig; + char *tmp_str; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + orig = ldb_msg_new(test_ctx); + assert_non_null(orig); + + orig->dn = ldb_dn_new(orig, test_ctx->domain->sysdb->ldb, + "cn=somedn,dc=example,dc=com"); + assert_non_null(orig->dn); + + tmp_str = talloc_strdup(orig, "ORIGNAME"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(orig, SYSDB_NAME, tmp_str); + assert_int_equal(ret, EOK); + + tmp_str = talloc_strdup(orig, "ORIGGECOS"); + assert_non_null(tmp_str); + ret = ldb_msg_add_string(orig, SYSDB_GECOS, tmp_str); + assert_int_equal(ret, EOK); + + test_ctx->domain->has_views = true; + test_ctx->domain->view_name = "NON-LOCAL"; + + ret = sysdb_add_overrides_to_object(test_ctx->domain, orig, NULL, NULL); + assert_int_equal(ret, ENOENT); +} + +void test_split_ipa_anchor(void **state) +{ + int ret; + char *dom; + char *uuid; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + ret = split_ipa_anchor(test_ctx, NULL, &dom, &uuid); + assert_int_equal(ret, EINVAL); + + ret = split_ipa_anchor(test_ctx, "fwfkwjfkw", &dom, &uuid); + assert_int_equal(ret, ENOMSG); + + ret = split_ipa_anchor(test_ctx, ":IPA:", &dom, &uuid); + assert_int_equal(ret, EINVAL); + + ret = split_ipa_anchor(test_ctx, ":IPA:abc", &dom, &uuid); + assert_int_equal(ret, EINVAL); + + ret = split_ipa_anchor(test_ctx, ":IPA:abc:", &dom, &uuid); + assert_int_equal(ret, EINVAL); + + ret = split_ipa_anchor(test_ctx, ":IPA:abc:def", &dom, &uuid); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "abc"); + assert_string_equal(uuid, "def"); +} + +void test_sysdb_delete_view_tree(void **state) +{ + int ret; + struct ldb_message *msg; + struct ldb_message **msgs = NULL; + struct sysdb_attrs *attrs; + size_t count; + struct ldb_dn *views_dn; + char *name; + + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + test_ctx->domain->mpg_mode = MPG_DISABLED; + + ret = sysdb_update_view_name(test_ctx->domain->sysdb, TEST_VIEW_NAME); + assert_int_equal(ret, EOK); + + name = sss_create_internal_fqname(test_ctx, TEST_USER_NAME, + test_ctx->domain->name); + assert_non_null(name); + + ret = sysdb_store_user(test_ctx->domain, name, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_GECOS, + TEST_USER_HOMEDIR, TEST_USER_SHELL, NULL, NULL, NULL, + 0,0); + assert_int_equal(ret, EOK); + + ret = sysdb_search_user_by_name(test_ctx, test_ctx->domain, name, + NULL, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID, + TEST_ANCHOR_PREFIX TEST_USER_SID); + assert_int_equal(ret, EOK); + + ret = sysdb_store_override(test_ctx->domain, TEST_VIEW_NAME, + SYSDB_MEMBER_USER, attrs, msg->dn); + assert_int_equal(ret, EOK); + + views_dn = ldb_dn_new(test_ctx, test_ctx->domain->sysdb->ldb, + SYSDB_TMPL_VIEW_BASE); + assert_non_null(views_dn); + + ret = sysdb_search_entry(test_ctx, test_ctx->domain->sysdb, views_dn, + LDB_SCOPE_SUBTREE, NULL, NULL, &count, &msgs); + assert_int_equal(ret, EOK); + assert_true(count > 1); + assert_non_null(msgs); + + ret = sysdb_delete_view_tree(test_ctx->domain->sysdb, TEST_VIEW_NAME); + assert_int_equal(ret, EOK); + + ret = sysdb_search_entry(test_ctx, test_ctx->domain->sysdb, views_dn, + LDB_SCOPE_SUBTREE, NULL, NULL, &count, &msgs); + assert_int_equal(ret, EOK); + assert_int_equal(count, 1); + assert_true(ldb_dn_compare(views_dn, msgs[0]->dn) == 0); + +} + +void test_sysdb_invalidate_overrides(void **state) +{ + int ret; + struct ldb_message *msg; + struct sysdb_attrs *attrs; + struct ldb_dn *views_dn; + char *name; + const char *user_attrs[] = { SYSDB_NAME, + SYSDB_CACHE_EXPIRE, + SYSDB_OVERRIDE_DN, + NULL}; + + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + test_ctx->domain->mpg_mode = MPG_DISABLED; + name = sss_create_internal_fqname(test_ctx, TEST_USER_NAME, + test_ctx->domain->name); + assert_non_null(name); + + + ret = sysdb_update_view_name(test_ctx->domain->sysdb, TEST_VIEW_NAME); + assert_int_equal(ret, EOK); + + ret = sysdb_store_user(test_ctx->domain, name, NULL, + TEST_USER_UID, TEST_USER_GID, TEST_USER_GECOS, + TEST_USER_HOMEDIR, TEST_USER_SHELL, NULL, NULL, NULL, + 10,0); + assert_int_equal(ret, EOK); + + ret = sysdb_search_user_by_name(test_ctx, test_ctx->domain, name, + NULL, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + + attrs = sysdb_new_attrs(test_ctx); + assert_non_null(attrs); + + ret = sysdb_attrs_add_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID, + TEST_ANCHOR_PREFIX TEST_USER_SID); + assert_int_equal(ret, EOK); + + ret = sysdb_store_override(test_ctx->domain, TEST_VIEW_NAME, + SYSDB_MEMBER_USER, attrs, msg->dn); + assert_int_equal(ret, EOK); + + views_dn = ldb_dn_new(test_ctx, test_ctx->domain->sysdb->ldb, + SYSDB_TMPL_VIEW_BASE); + assert_non_null(views_dn); + + ret = sysdb_delete_view_tree(test_ctx->domain->sysdb, TEST_VIEW_NAME); + assert_int_equal(ret, EOK); + + ret = sysdb_search_user_by_name(test_ctx, test_ctx->domain, name, + user_attrs, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + assert_true(ldb_msg_find_attr_as_uint64(msg, SYSDB_CACHE_EXPIRE, 0) > 1); + assert_non_null(ldb_msg_find_attr_as_string(msg, SYSDB_OVERRIDE_DN, NULL)); + + ret = sysdb_invalidate_overrides(test_ctx->domain->sysdb); + assert_int_equal(ret, EOK); + + ret = sysdb_search_user_by_name(test_ctx, test_ctx->domain, name, + user_attrs, &msg); + assert_int_equal(ret, EOK); + assert_non_null(msg); + assert_int_equal(ldb_msg_find_attr_as_uint64(msg, SYSDB_CACHE_EXPIRE, 0), + 1); + assert_null(ldb_msg_find_attr_as_string(msg, SYSDB_OVERRIDE_DN, NULL)); + + ret = sysdb_delete_user(test_ctx->domain, name, 0); + assert_int_equal(ret, EOK); +} + +static const char *users[] = { "alice", "bob", "barney", NULL }; + +static void enum_test_user_override(struct sysdb_test_ctx *test_ctx, + const char *name) +{ + int ret; + struct sysdb_attrs *attrs; + struct ldb_dn *dn; + TALLOC_CTX *tmp_ctx; + const char *anchor; + const char *override_gecos; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + attrs = sysdb_new_attrs(tmp_ctx); + assert_non_null(attrs); + + dn = sysdb_user_dn(tmp_ctx, test_ctx->domain, name); + assert_non_null(dn); + + anchor = talloc_asprintf(tmp_ctx, "%s%s", TEST_ANCHOR_PREFIX, name); + ret = sysdb_attrs_add_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID, anchor); + assert_int_equal(ret, EOK); + + override_gecos = talloc_asprintf(attrs, "%s_GECOS_OVERRIDE", name); + ret = sysdb_attrs_add_string(attrs, SYSDB_GECOS, override_gecos); + assert_int_equal(ret, EOK); + + ret = sysdb_store_override(test_ctx->domain, TEST_VIEW_NAME, + SYSDB_MEMBER_USER, attrs, dn); + assert_int_equal(ret, EOK); + + talloc_free(tmp_ctx); +} + +static void enum_test_add_users(struct sysdb_test_ctx *test_ctx, + const char *usernames[]) +{ + int i; + int ret; + struct sysdb_attrs *attrs; + char *fqname = NULL; + + for (i = 0; usernames[i] != NULL; i++) { + attrs = talloc(test_ctx, struct sysdb_attrs); + assert_non_null(attrs); + fqname = sss_create_internal_fqname(test_ctx, usernames[i], + test_ctx->domain->name); + assert_non_null(fqname); + + ret = sysdb_store_user(test_ctx->domain, fqname, + NULL, 1234 + i, 1234 + i, fqname, "/", "/bin/sh", + NULL, NULL, NULL, 1, 1234 + i); + assert_int_equal(ret, EOK); + + enum_test_user_override(test_ctx, fqname); + + talloc_free(attrs); + talloc_free(fqname); + } +} + +static void enum_test_del_users(struct sysdb_test_ctx *test_ctx, + const char *usernames[]) +{ + int i; + int ret; + char *fqname = NULL; + + for (i = 0; usernames[i] != NULL; i++) { + fqname = sss_create_internal_fqname(test_ctx, usernames[i], + test_ctx->domain->name); + assert_non_null(fqname); + + ret = sysdb_delete_user(test_ctx->domain, fqname, 0); + talloc_free(fqname); + if (ret != EOK && ret != ENOENT) { + fail(); + } + } +} + +static int test_enum_users_setup(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + ret = setup_sysdb_enum_tests(&test_ctx); + assert_int_equal(ret, EOK); + + enum_test_add_users(test_ctx, users); + + *state = (void *) test_ctx; + return 0; +} + +static int cmp_func(const void *a, const void *b) +{ + const char *str1; + const char *str2; + struct ldb_message *msg1 = *(struct ldb_message **)discard_const(a); + struct ldb_message *msg2 = *(struct ldb_message **)discard_const(b); + + str1 = ldb_msg_find_attr_as_string(msg1, SYSDB_NAME, NULL); + str2 = ldb_msg_find_attr_as_string(msg2, SYSDB_NAME, NULL); + + return strcmp(str1, str2); +} + +/* Make the order of ldb results deterministic */ +static void order_ldb_res_msgs(struct ldb_result *res) +{ + if (res == NULL || res->count < 2) { + /* Nothing to do */ + return; + } + + qsort(res->msgs, res->count, sizeof(struct ldb_message *), cmp_func); + return; +} + +static void assert_user_attrs(struct ldb_message *msg, + struct sss_domain_info *dom, + const char *shortname, + bool has_views) +{ + const char *str; + char *fqname; + + fqname = sss_create_internal_fqname(msg, shortname, dom->name); + assert_non_null(fqname); + + str = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + assert_string_equal(str, fqname); + str = ldb_msg_find_attr_as_string(msg, SYSDB_GECOS, NULL); + assert_string_equal(str, fqname); + + str = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_GECOS, NULL); + if (has_views) { + char *override; + + assert_non_null(str); + override = talloc_asprintf(msg, "%s_GECOS_OVERRIDE", fqname); + assert_non_null(override); + + assert_string_equal(str, override); + talloc_free(override); + } else { + assert_null(str); + } + + talloc_free(fqname); +} + +static int test_enum_users_teardown(void **state) +{ + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + enum_test_del_users(test_ctx, users); + return test_sysdb_teardown(state); +} + +static void check_enumpwent(int ret, struct sss_domain_info *dom, + struct ldb_result *res, bool views) +{ + assert_int_equal(ret, EOK); + assert_int_equal(res->count, N_ELEMENTS(users)-1); + + order_ldb_res_msgs(res); + assert_user_attrs(res->msgs[0], dom, "alice", views); + assert_user_attrs(res->msgs[1], dom, "barney", views); + assert_user_attrs(res->msgs[2], dom, "bob", views); +} + +static void test_sysdb_enumpwent(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + struct ldb_result *res; + + ret = sysdb_enumpwent(test_ctx, test_ctx->domain, &res); + check_enumpwent(ret, test_ctx->domain, res, false); +} + +static void test_sysdb_enumpwent_views(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + struct ldb_result *res; + + ret = sysdb_enumpwent_with_views(test_ctx, test_ctx->domain, &res); + check_enumpwent(ret, test_ctx->domain, res, true); +} + +static void test_sysdb_enumpwent_filter(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + struct ldb_result *res; + char *addtl_filter; + + ret = sysdb_enumpwent_filter(test_ctx, test_ctx->domain, SYSDB_UIDNUM, "1234", + 0, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_user_attrs(res->msgs[0], test_ctx->domain, "alice", false); + + ret = sysdb_enumpwent_filter(test_ctx, test_ctx->domain, SYSDB_NAME, "a*", + 0, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_user_attrs(res->msgs[0], test_ctx->domain, "alice", false); + + ret = sysdb_enumpwent_filter(test_ctx, test_ctx->domain, SYSDB_NAME, "b*", + 0, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 2); + order_ldb_res_msgs(res); + assert_user_attrs(res->msgs[0], test_ctx->domain, "barney", false); + assert_user_attrs(res->msgs[1], test_ctx->domain, "bob", false); + + ret = sysdb_enumpwent_filter(test_ctx, test_ctx->domain, SYSDB_NAME, "c*", + 0, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + + ret = sysdb_enumpwent_filter(test_ctx, test_ctx->domain, SYSDB_NAME, "*", + 0, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, N_ELEMENTS(users)-1); + + /* Test searching based on time as well */ + addtl_filter = talloc_asprintf(test_ctx, "(%s<=%d)", + SYSDB_LAST_UPDATE, 1233); + ret = sysdb_enumpwent_filter(test_ctx, test_ctx->domain, SYSDB_NAME, "a*", + addtl_filter, &res); + talloc_free(addtl_filter); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + + addtl_filter = talloc_asprintf(test_ctx, "(%s<=%d)", + SYSDB_LAST_UPDATE, 1234); + ret = sysdb_enumpwent_filter(test_ctx, test_ctx->domain, SYSDB_NAME, "a*", + addtl_filter, &res); + talloc_free(addtl_filter); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_user_attrs(res->msgs[0], test_ctx->domain, "alice", false); +} + +static void test_sysdb_enumpwent_filter_views(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + struct ldb_result *res; + char *addtl_filter; + + ret = sysdb_enumpwent_filter_with_views(test_ctx, test_ctx->domain, + SYSDB_UIDNUM, "1234", NULL, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_user_attrs(res->msgs[0], test_ctx->domain, "alice", true); + + ret = sysdb_enumpwent_filter_with_views(test_ctx, test_ctx->domain, + SYSDB_NAME, "a*", NULL, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_user_attrs(res->msgs[0], test_ctx->domain, "alice", true); + + ret = sysdb_enumpwent_filter_with_views(test_ctx, test_ctx->domain, + SYSDB_NAME, "b*", NULL, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 2); + order_ldb_res_msgs(res); + assert_user_attrs(res->msgs[0], test_ctx->domain, "barney", true); + assert_user_attrs(res->msgs[1], test_ctx->domain, "bob", true); + + addtl_filter = talloc_asprintf(test_ctx, "(%s<=%d)", + SYSDB_LAST_UPDATE, 1235); + ret = sysdb_enumpwent_filter_with_views(test_ctx, test_ctx->domain, + SYSDB_NAME, "b*", addtl_filter, &res); + talloc_free(addtl_filter); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_user_attrs(res->msgs[0], test_ctx->domain, "bob", true); + + ret = sysdb_enumpwent_filter_with_views(test_ctx, test_ctx->domain, + SYSDB_NAME, "c*", NULL, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + + ret = sysdb_enumpwent_filter_with_views(test_ctx, test_ctx->domain, + SYSDB_NAME, "*", NULL, &res); + check_enumpwent(ret, test_ctx->domain, res, true); +} + +static const char *groups[] = { "one", "two", "three", NULL }; + +static void enum_test_group_override(struct sysdb_test_ctx *test_ctx, + const char *name, + unsigned override_gid) +{ + int ret; + struct sysdb_attrs *attrs; + struct ldb_dn *dn; + TALLOC_CTX *tmp_ctx; + const char *anchor; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + attrs = sysdb_new_attrs(tmp_ctx); + assert_non_null(attrs); + + dn = sysdb_group_dn(tmp_ctx, test_ctx->domain, name); + assert_non_null(dn); + + anchor = talloc_asprintf(tmp_ctx, "%s%s", TEST_ANCHOR_PREFIX, name); + ret = sysdb_attrs_add_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID, anchor); + assert_int_equal(ret, EOK); + + ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, override_gid); + assert_int_equal(ret, EOK); + + ret = sysdb_store_override(test_ctx->domain, TEST_VIEW_NAME, + SYSDB_MEMBER_GROUP, attrs, dn); + assert_int_equal(ret, EOK); + + talloc_free(tmp_ctx); +} + +static void enum_test_add_groups(struct sysdb_test_ctx *test_ctx, + const char *groupnames[]) +{ + int i; + int ret; + struct sysdb_attrs *attrs; + char *gr_name; + + for (i = 0; groupnames[i] != NULL; i++) { + attrs = talloc(test_ctx, struct sysdb_attrs); + assert_non_null(attrs); + + gr_name = sss_create_internal_fqname(test_ctx, groupnames[i], + test_ctx->domain->name); + ret = sysdb_store_group(test_ctx->domain, gr_name, + 0, NULL, 1, 1234 + i); + assert_int_equal(ret, EOK); + + enum_test_group_override(test_ctx, gr_name, + TEST_GID_OVERRIDE_BASE + i); + talloc_free(attrs); + } +} + +static void enum_test_del_groups(struct sss_domain_info *dom, + const char *groupnames[]) +{ + int i; + int ret; + + for (i = 0; groupnames[i] != NULL; i++) { + ret = sysdb_delete_group(dom, groupnames[i], 0); + if (ret != EOK && ret != ENOENT) { + fail(); + } + } +} + +static int test_enum_groups_setup(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + ret = setup_sysdb_enum_tests(&test_ctx); + assert_int_equal(ret, EOK); + + enum_test_add_groups(test_ctx, groups); + + *state = (void *) test_ctx; + return 0; +} + +static int test_enum_groups_teardown(void **state) +{ + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + + enum_test_del_groups(test_ctx->domain, groups); + return test_sysdb_teardown(state); +} + +static void assert_group_attrs(struct ldb_message *msg, + struct sss_domain_info *dom, + const char *shortname, + unsigned expected_override_gid) +{ + const char *str; + unsigned gid; + char *fqname; + + fqname = sss_create_internal_fqname(msg, shortname, dom->name); + assert_non_null(fqname); + + str = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + assert_string_equal(str, fqname); + + if (expected_override_gid) { + gid = ldb_msg_find_attr_as_uint64(msg, + OVERRIDE_PREFIX SYSDB_GIDNUM, 0); + assert_int_equal(gid, expected_override_gid); + } +} + +static void check_enumgrent(int ret, struct sss_domain_info *dom, + struct ldb_result *res, bool views) +{ + assert_int_equal(ret, EOK); + assert_int_equal(res->count, N_ELEMENTS(groups)-1); + order_ldb_res_msgs(res); + assert_group_attrs(res->msgs[0], dom, "one", + views ? TEST_GID_OVERRIDE_BASE : 0); + assert_group_attrs(res->msgs[1], dom, "three", + views ? TEST_GID_OVERRIDE_BASE + 2 : 0); + assert_group_attrs(res->msgs[2], dom, "two", + views ? TEST_GID_OVERRIDE_BASE + 1 : 0); +} + +static void test_sysdb_enumgrent(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + struct ldb_result *res; + + ret = sysdb_enumgrent(test_ctx, test_ctx->domain, &res); + check_enumgrent(ret, test_ctx->domain, res, false); +} + +static void test_sysdb_enumgrent_views(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + struct ldb_result *res; + + ret = sysdb_enumgrent_with_views(test_ctx, test_ctx->domain, &res); + check_enumgrent(ret, test_ctx->domain, res, true); +} + +static void test_sysdb_enumgrent_filter(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + struct ldb_result *res; + char *addtl_filter; + + ret = sysdb_enumgrent_filter(test_ctx, test_ctx->domain, "o*", 0, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_group_attrs(res->msgs[0], test_ctx->domain, "one", 0); + + ret = sysdb_enumgrent_filter(test_ctx, test_ctx->domain, "t*", 0, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 2); + order_ldb_res_msgs(res); + assert_group_attrs(res->msgs[0], test_ctx->domain, "three", 0); + assert_group_attrs(res->msgs[1], test_ctx->domain, "two", 0); + + ret = sysdb_enumgrent_filter(test_ctx, test_ctx->domain, "x*", 0, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + + ret = sysdb_enumgrent_filter(test_ctx, test_ctx->domain, "*", 0, &res); + check_enumgrent(ret, test_ctx->domain, res, false); + + addtl_filter = talloc_asprintf(test_ctx, "(%s<=%d)", + SYSDB_LAST_UPDATE, 1233); + ret = sysdb_enumgrent_filter(test_ctx, test_ctx->domain, "o*", addtl_filter, &res); + talloc_free(addtl_filter); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + + addtl_filter = talloc_asprintf(test_ctx, "(%s<=%d)", + SYSDB_LAST_UPDATE, 1234); + ret = sysdb_enumgrent_filter(test_ctx, test_ctx->domain, "o*", addtl_filter, &res); + talloc_free(addtl_filter); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_group_attrs(res->msgs[0], test_ctx->domain, "one", 0); + +} + +static void test_sysdb_enumgrent_filter_views(void **state) +{ + int ret; + struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state, + struct sysdb_test_ctx); + struct ldb_result *res; + char *addtl_filter; + + ret = sysdb_enumgrent_filter_with_views(test_ctx, test_ctx->domain, + "o*", NULL, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_group_attrs(res->msgs[0], test_ctx->domain, + "one", TEST_GID_OVERRIDE_BASE); + + ret = sysdb_enumgrent_filter_with_views(test_ctx, test_ctx->domain, + "t*", NULL, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 2); + order_ldb_res_msgs(res); + assert_group_attrs(res->msgs[0], test_ctx->domain, + "three", TEST_GID_OVERRIDE_BASE + 2); + assert_group_attrs(res->msgs[1], test_ctx->domain, "two", + TEST_GID_OVERRIDE_BASE + 1); + + addtl_filter = talloc_asprintf(test_ctx, "(%s<=%d)", + SYSDB_LAST_UPDATE, 1235); + ret = sysdb_enumgrent_filter_with_views(test_ctx, test_ctx->domain, + "t*", addtl_filter, &res); + talloc_free(addtl_filter); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 1); + assert_group_attrs(res->msgs[0], test_ctx->domain, "two", + TEST_GID_OVERRIDE_BASE + 1); + + ret = sysdb_enumgrent_filter_with_views(test_ctx, test_ctx->domain, + "x*", NULL, &res); + assert_int_equal(ret, EOK); + assert_int_equal(res->count, 0); + + ret = sysdb_enumgrent_filter_with_views(test_ctx, test_ctx->domain, + "*", NULL, &res); + check_enumgrent(ret, test_ctx->domain, res, true); +} + +int main(int argc, const char *argv[]) +{ + int rv; + int no_cleanup = 0; + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, + _("Do not delete the test database after a test run"), NULL }, + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sysdb_store_override, + test_sysdb_setup, test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_add_overrides_to_object, + test_sysdb_setup, test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_add_overrides_to_object_local, + test_sysdb_setup, test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_add_overrides_to_object_missing_overridedn, + test_sysdb_setup, test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_split_ipa_anchor, + test_sysdb_setup, test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_delete_view_tree, + test_sysdb_setup, test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_invalidate_overrides, + test_sysdb_setup, test_sysdb_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_enumpwent, + test_enum_users_setup, + test_enum_users_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_enumpwent_views, + test_enum_users_setup, + test_enum_users_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_enumpwent_filter, + test_enum_users_setup, + test_enum_users_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_enumpwent_filter_views, + test_enum_users_setup, + test_enum_users_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_enumgrent, + test_enum_groups_setup, + test_enum_groups_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_enumgrent_views, + test_enum_groups_setup, + test_enum_groups_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_enumgrent_filter, + test_enum_groups_setup, + test_enum_groups_teardown), + cmocka_unit_test_setup_teardown(test_sysdb_enumgrent_filter_views, + test_enum_groups_setup, + test_enum_groups_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_FILE, "FILES"); + test_dom_suite_setup(TESTS_PATH); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + if (rv == 0 && no_cleanup == 0) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_FILE, "FILES"); + } + return rv; +} diff --git a/src/tests/cmocka/test_tools_colondb.c b/src/tests/cmocka/test_tools_colondb.c new file mode 100644 index 0000000..279b0a5 --- /dev/null +++ b/src/tests/cmocka/test_tools_colondb.c @@ -0,0 +1,417 @@ +/* + Authors: + Petr ト憩ch <pcech@redhat.com> + + Copyright (C) 2015 Red Hat + + 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 <talloc.h> +#include <errno.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "src/tools/common/sss_colondb.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TESTS_FILE "test_colondb.ldb" + +const char *TEST_STRING1 = "white"; +const int TEST_INT1 = 12; + +const char *TEST_STRING2 = "black"; +const int TEST_INT2 = 34; + +static void create_dir(const char *path) +{ + errno_t ret; + + errno = 0; + ret = mkdir(path, 0775); + assert_return_code(ret, errno); +} + +static void create_empty_file(TALLOC_CTX *test_ctx, const char *path, + const char *name) +{ + TALLOC_CTX *tmp_ctx = NULL; + char *file_name = NULL; + FILE *fp = NULL; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + create_dir(path); + + file_name = talloc_asprintf(tmp_ctx, "%s/%s", path, name); + assert_non_null(file_name); + + fp = fopen(file_name, "w"); + assert_non_null(fp); + fclose(fp); + + talloc_free(tmp_ctx); +} + +static void create_nonempty_file(TALLOC_CTX *test_ctx, + const char *path, const char *name) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct sss_colondb *db = NULL; + errno_t ret; + struct sss_colondb_write_field table[] = { + { SSS_COLONDB_STRING, { .str = TEST_STRING2 } }, + { SSS_COLONDB_UINT32, { .uint32 = TEST_INT2 } }, + { SSS_COLONDB_SENTINEL, { 0 } } + }; + + tmp_ctx = talloc_new(test_ctx); + assert_non_null(tmp_ctx); + + create_empty_file(test_ctx, TESTS_PATH, TESTS_FILE); + + db = sss_colondb_open(tmp_ctx, SSS_COLONDB_WRITE, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + + ret = sss_colondb_writeline(db, table); + assert_int_equal(ret, EOK); + + talloc_free(db); + talloc_free(tmp_ctx); +} + +static int setup(void **state, int file_state) +{ + TALLOC_CTX *test_ctx = NULL; + + assert_true(leak_check_setup()); + + test_ctx = talloc_new(global_talloc_context); + assert_non_null(test_ctx); + + switch (file_state) { + case 0: + break; + case 1: + create_empty_file(test_ctx, TESTS_PATH, TESTS_FILE); + break; + case 2: + create_nonempty_file(test_ctx, TESTS_PATH, TESTS_FILE); + break; + default: + break; + } + + check_leaks_push(test_ctx); + *state = test_ctx; + + return 0; +} + +static int without_file_setup(void **state) +{ + return setup(state, 0); +} + +static int with_empty_file_setup(void **state) +{ + return setup(state, 1); +} + +static int with_nonempty_file_setup(void **state) +{ + return setup(state, 2); +} + +static int teardown(void **state) +{ + errno_t ret; + + errno = 0; + ret = unlink(TESTS_PATH "/" TESTS_FILE); + if (ret != 0) { + assert_int_equal(errno, ENOENT); + } + + assert_true(check_leaks_pop(*state)); + talloc_zfree(*state); + + test_dom_suite_cleanup(TESTS_PATH, NULL, NULL); + assert_true(leak_check_teardown()); + + return 0; +} + +void test_open_nonexist_for_read(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_READ, + TESTS_PATH "/" TESTS_FILE); + assert_null(db); + talloc_free(db); +} + +void test_open_nonexist_for_write(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE, + TESTS_PATH "/" TESTS_FILE); + assert_null(db); + talloc_free(db); +} + +void test_open_exist_for_read(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_READ, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + talloc_free(db); +} + +void test_open_exist_for_write(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + talloc_free(db); +} + +void test_open_nonempty_for_read(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_READ, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + talloc_free(db); +} + +void test_open_nonempty_for_write(void **state) +{ + + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + talloc_free(db); +} + +void test_write_to_empty(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + struct sss_colondb_write_field table[] = { + { SSS_COLONDB_STRING, { .str = TEST_STRING1 } }, + { SSS_COLONDB_UINT32, { .uint32 = TEST_INT1 } }, + { SSS_COLONDB_SENTINEL, { 0 } } + }; + errno_t ret; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + + ret = sss_colondb_writeline(db, table); + assert_int_equal(ret, 0); + + talloc_free(db); +} + +void test_write_to_nonempty(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + struct sss_colondb_write_field table[] = { + { SSS_COLONDB_STRING, { .str = TEST_STRING1 } }, + { SSS_COLONDB_UINT32, { .uint32 = TEST_INT1 } }, + { SSS_COLONDB_SENTINEL, { 0 } } + }; + errno_t ret; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + + ret = sss_colondb_writeline(db, table); + assert_int_equal(ret, 0); + + talloc_free(db); +} + +void test_read_from_nonempty(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + errno_t ret; + const char *string = NULL; + uint32_t number; + struct sss_colondb_read_field table[] = { + { SSS_COLONDB_STRING, { .str = &string } }, + { SSS_COLONDB_UINT32, { .uint32 = &number } }, + { SSS_COLONDB_SENTINEL, { 0 } } + }; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_READ, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + + ret = sss_colondb_readline(test_ctx, db, table); + assert_int_equal(ret, 0); + assert_string_equal(string, TEST_STRING2); + assert_int_equal(number, TEST_INT2); + + talloc_zfree(string); + talloc_free(db); +} + +void test_read_from_empty(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + errno_t ret; + const char *string; + uint32_t number; + struct sss_colondb_read_field table[] = { + { SSS_COLONDB_STRING, { .str = &string } }, + { SSS_COLONDB_UINT32, { .uint32 = &number } }, + { SSS_COLONDB_SENTINEL, { 0 } } + }; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_READ, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + + ret = sss_colondb_readline(test_ctx, db, table); + assert_int_equal(ret, EOF); + + talloc_free(db); +} + +void test_write_read(void **state) +{ + TALLOC_CTX *test_ctx = *state; + struct sss_colondb *db = NULL; + errno_t ret; + const char *string = NULL; + uint32_t number; + struct sss_colondb_write_field table_in[] = { + { SSS_COLONDB_STRING, { .str = TEST_STRING2 } }, + { SSS_COLONDB_UINT32, { .uint32 = TEST_INT2 } }, + { SSS_COLONDB_SENTINEL, { 0 } } + }; + struct sss_colondb_read_field table_out[] = { + { SSS_COLONDB_STRING, { .str = &string } }, + { SSS_COLONDB_UINT32, { .uint32 = &number } }, + { SSS_COLONDB_SENTINEL, { 0 } } + }; + + db = sss_colondb_open(test_ctx, SSS_COLONDB_WRITE, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + + ret = sss_colondb_writeline(db, table_in); + assert_int_equal(ret, 0); + + talloc_free(db); + + db = sss_colondb_open(test_ctx, SSS_COLONDB_READ, + TESTS_PATH "/" TESTS_FILE); + assert_non_null(db); + + ret = sss_colondb_readline(test_ctx, db, table_out); + assert_int_equal(ret, 0); + assert_string_equal(string, TEST_STRING2); + assert_int_equal(number, TEST_INT2); + + talloc_zfree(string); + talloc_free(db); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_open_nonexist_for_read, + without_file_setup, teardown), + cmocka_unit_test_setup_teardown(test_open_nonexist_for_write, + without_file_setup, teardown), + cmocka_unit_test_setup_teardown(test_open_exist_for_read, + with_empty_file_setup, teardown), + cmocka_unit_test_setup_teardown(test_open_exist_for_write, + with_empty_file_setup, teardown), + cmocka_unit_test_setup_teardown(test_open_nonempty_for_read, + with_nonempty_file_setup, teardown), + cmocka_unit_test_setup_teardown(test_open_nonempty_for_write, + with_nonempty_file_setup, teardown), + + cmocka_unit_test_setup_teardown(test_write_to_empty, + with_empty_file_setup, teardown), + cmocka_unit_test_setup_teardown(test_write_to_nonempty, + with_nonempty_file_setup, teardown), + + cmocka_unit_test_setup_teardown(test_read_from_empty, + with_empty_file_setup, teardown), + cmocka_unit_test_setup_teardown(test_read_from_nonempty, + with_nonempty_file_setup, teardown), + + cmocka_unit_test_setup_teardown(test_write_read, + with_empty_file_setup, teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), + poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, NULL, NULL); + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c new file mode 100644 index 0000000..760e713 --- /dev/null +++ b/src/tests/cmocka/test_utils.c @@ -0,0 +1,2528 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2013 Red Hat + + SSSD tests: Tests for utility functions + + 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/>. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <popt.h> + +#include "tests/cmocka/common_mock.h" +#include "util/sss_nss.h" +#include "p11_child/p11_child.h" +#include "test_utils.h" + +#define TESTS_PATH "tp_" BASE_FILE_STEM +#define TEST_CONF_DB "test_utils_conf.ldb" +#define TEST_DOM_NAME "utils_test.ldb" + +#define DOM_COUNT 10 +#define DOMNAME_TMPL "name_%zu.dom" +#define FLATNAME_TMPL "name_%zu" +#define SID_TMPL "S-1-5-21-1-2-%zu" + +#define MACRO_EXPAND(tok) #tok +#define STR(tok) MACRO_EXPAND(tok) + +#define USERNAME "sssduser" +#define FIRST_LETTER "s" +#define UID 1234 +#define DOMAIN "sssddomain" +#define ORIGINAL_HOME "/home/USER" +#define LOWERCASE_HOME "/home/user" +#define FLATNAME "flatname" +#define HOMEDIR_SUBSTR "/mnt/home" + +#define DUMMY "dummy" +#define DUMMY2 "dummy2" + +struct dom_list_test_ctx { + size_t dom_count; + struct sss_domain_info *dom_list; +}; + +static int setup_dom_list_with_subdomains(void **state) +{ + struct dom_list_test_ctx *test_ctx; + struct sss_domain_info *dom = NULL; + struct sss_domain_info *c = NULL; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct dom_list_test_ctx); + assert_non_null(test_ctx); + + dom = talloc_zero(test_ctx, struct sss_domain_info); + assert_non_null(dom); + + dom->name = talloc_asprintf(dom, "configured.dom"); + assert_non_null(dom->name); + + dom->realm = talloc_asprintf(dom, "CONFIGURED.DOM"); + assert_non_null(dom->realm); + + dom->flat_name = talloc_asprintf(dom, "CONFIGURED"); + assert_non_null(dom->flat_name); + + dom->domain_id = talloc_asprintf(dom, "S-1-5-21-1-2-1"); + assert_non_null(dom->domain_id); + + DLIST_ADD(test_ctx->dom_list, dom); + + c = talloc_zero(test_ctx, struct sss_domain_info); + assert_non_null(c); + + c->name = talloc_asprintf(c, "subdom1.dom"); + assert_non_null(c->name); + + c->realm = talloc_asprintf(c, "SUBDOM1.DOM"); + assert_non_null(c->realm); + + c->flat_name = talloc_asprintf(c, "subdom1"); + assert_non_null(c->flat_name); + + c->domain_id = talloc_asprintf(c, "S-1-5-21-1-2-2"); + assert_non_null(c->domain_id); + + c->parent = dom; + + DLIST_ADD_END(test_ctx->dom_list, c, struct sss_domain_info *); + + c = talloc_zero(test_ctx, struct sss_domain_info); + assert_non_null(c); + + c->name = talloc_asprintf(c, "subdom2.dom"); + assert_non_null(c->name); + + c->realm = talloc_asprintf(c, "SUBDOM2.DOM"); + assert_non_null(c->realm); + + c->flat_name = talloc_asprintf(c, "subdom2"); + assert_non_null(c->flat_name); + + c->domain_id = talloc_asprintf(c, "S-1-5-21-1-2-3"); + assert_non_null(c->domain_id); + + c->parent = dom; + + DLIST_ADD_END(test_ctx->dom_list, c, struct sss_domain_info *); + + c = talloc_zero(test_ctx, struct sss_domain_info); + assert_non_null(c); + + c->name = talloc_asprintf(c, "subdom3.dom"); + assert_non_null(c->name); + + c->realm = talloc_asprintf(c, "SUBDOM3.DOM"); + assert_non_null(c->realm); + + c->flat_name = talloc_asprintf(c, "subdom3"); + assert_non_null(c->flat_name); + + c->domain_id = talloc_asprintf(c, "S-1-5-21-1-2-4"); + assert_non_null(c->domain_id); + + c->parent = dom; + + DLIST_ADD_END(test_ctx->dom_list, c, struct sss_domain_info *); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int setup_dom_list(void **state) +{ + struct dom_list_test_ctx *test_ctx; + struct sss_domain_info *dom = NULL; + size_t c; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct dom_list_test_ctx); + assert_non_null(test_ctx); + + test_ctx->dom_count = DOM_COUNT; + + for (c = 0; c < test_ctx->dom_count; c++) { + dom = talloc_zero(test_ctx, struct sss_domain_info); + assert_non_null(dom); + + dom->name = talloc_asprintf(dom, DOMNAME_TMPL, c); + assert_non_null(dom->name); + + dom->flat_name = talloc_asprintf(dom, FLATNAME_TMPL, c); + assert_non_null(dom->flat_name); + + dom->domain_id = talloc_asprintf(dom, SID_TMPL, c); + assert_non_null(dom->domain_id); + + DLIST_ADD(test_ctx->dom_list, dom); + } + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int teardown_dom_list(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + if (test_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return 1; + } + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void test_find_domain_by_name_null(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + + dom = find_domain_by_name(NULL, NULL, false); + assert_null(dom); + + dom = find_domain_by_name(test_ctx->dom_list, NULL, false); + assert_null(dom); + + dom = find_domain_by_name(NULL, "test", false); + assert_null(dom); +} + +void test_find_domain_by_name(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + size_t c; + char *name; + char *flat_name; + char *sid; + + for (c = 0; c < test_ctx->dom_count; c++) { + name = talloc_asprintf(global_talloc_context, DOMNAME_TMPL, c); + assert_non_null(name); + + flat_name = talloc_asprintf(global_talloc_context, FLATNAME_TMPL, c); + assert_non_null(flat_name); + + sid = talloc_asprintf(global_talloc_context, SID_TMPL, c); + assert_non_null(sid); + + dom = find_domain_by_name(test_ctx->dom_list, name, false); + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + + dom = find_domain_by_name(test_ctx->dom_list, name, true); + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + + dom = find_domain_by_name(test_ctx->dom_list, flat_name, true); + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + + dom = find_domain_by_name(test_ctx->dom_list, flat_name, false); + assert_null(dom); + + talloc_free(name); + talloc_free(flat_name); + talloc_free(sid); + } +} + +void test_find_domain_by_name_missing_flat_name(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + size_t c; + char *name; + char *flat_name; + char *sid; + size_t mis; + + mis = test_ctx->dom_count/2; + assert_true((mis >= 1 && mis < test_ctx->dom_count)); + + dom = test_ctx->dom_list; + for (c = 0; c < mis; c++) { + assert_non_null(dom); + dom = dom->next; + } + assert_non_null(dom); + dom->flat_name = NULL; + + for (c = 0; c < test_ctx->dom_count; c++) { + name = talloc_asprintf(global_talloc_context, DOMNAME_TMPL, c); + assert_non_null(name); + + flat_name = talloc_asprintf(global_talloc_context, FLATNAME_TMPL, c); + assert_non_null(flat_name); + + sid = talloc_asprintf(global_talloc_context, SID_TMPL, c); + assert_non_null(sid); + + dom = find_domain_by_name(test_ctx->dom_list, name, true); + assert_non_null(dom); + assert_string_equal(name, dom->name); + if (c == mis - 1) { + assert_null(dom->flat_name); + } else { + assert_string_equal(flat_name, dom->flat_name); + } + assert_string_equal(sid, dom->domain_id); + + dom = find_domain_by_name(test_ctx->dom_list, name, false); + assert_non_null(dom); + assert_string_equal(name, dom->name); + if (c == mis - 1) { + assert_null(dom->flat_name); + } else { + assert_string_equal(flat_name, dom->flat_name); + } + assert_string_equal(sid, dom->domain_id); + + dom = find_domain_by_name(test_ctx->dom_list, flat_name, true); + if (c == mis - 1) { + assert_null(dom); + } else { + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + } + + dom = find_domain_by_name(test_ctx->dom_list, flat_name, false); + assert_null(dom); + + talloc_free(name); + talloc_free(flat_name); + talloc_free(sid); + } +} + +void test_find_domain_by_name_disabled(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + size_t c; + char *name; + char *flat_name; + char *sid; + size_t mis; + + mis = test_ctx->dom_count/2; + assert_true((mis >= 1 && mis < test_ctx->dom_count)); + + dom = test_ctx->dom_list; + for (c = 0; c < mis; c++) { + assert_non_null(dom); + dom = dom->next; + } + assert_non_null(dom); + sss_domain_set_state(dom, DOM_DISABLED); + + for (c = 0; c < test_ctx->dom_count; c++) { + name = talloc_asprintf(global_talloc_context, DOMNAME_TMPL, c); + assert_non_null(name); + + flat_name = talloc_asprintf(global_talloc_context, FLATNAME_TMPL, c); + assert_non_null(flat_name); + + sid = talloc_asprintf(global_talloc_context, SID_TMPL, c); + assert_non_null(sid); + + dom = find_domain_by_name(test_ctx->dom_list, name, true); + if (c == mis - 1) { + assert_null(dom); + } else { + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + } + + dom = find_domain_by_name(test_ctx->dom_list, name, false); + if (c == mis - 1) { + assert_null(dom); + } else { + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + } + + dom = find_domain_by_name(test_ctx->dom_list, flat_name, true); + if (c == mis - 1) { + assert_null(dom); + } else { + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + } + + dom = find_domain_by_name(test_ctx->dom_list, flat_name, false); + assert_null(dom); + + talloc_free(name); + talloc_free(flat_name); + talloc_free(sid); + } +} + +void test_find_domain_by_name_ex_disabled(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + struct sss_domain_info *disabled_dom; + size_t c; + size_t mis; + + mis = test_ctx->dom_count/2; + assert_true((mis >= 1 && mis < test_ctx->dom_count)); + + dom = test_ctx->dom_list; + for (c = 0; c < mis; c++) { + assert_non_null(dom); + dom = dom->next; + } + assert_non_null(dom); + sss_domain_set_state(dom, DOM_DISABLED); + disabled_dom = dom; + + dom = find_domain_by_name(test_ctx->dom_list, disabled_dom->name, true); + assert_null(dom); + + dom = find_domain_by_name_ex(test_ctx->dom_list, disabled_dom->name, true, + SSS_GND_DESCEND); + assert_null(dom); + + dom = find_domain_by_name_ex(test_ctx->dom_list, disabled_dom->name, true, + SSS_GND_DESCEND | SSS_GND_INCLUDE_DISABLED); + assert_non_null(dom); + assert_ptr_equal(disabled_dom, dom); + + dom = find_domain_by_name_ex(test_ctx->dom_list, disabled_dom->name, true, + SSS_GND_ALL_DOMAINS); + assert_non_null(dom); + assert_ptr_equal(disabled_dom, dom); +} + +void test_find_domain_by_object_name_ex(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + struct sss_domain_info *disabled_dom; + size_t c; + size_t mis; + char *obj_name; + + mis = test_ctx->dom_count/2; + assert_true((mis >= 1 && mis < test_ctx->dom_count)); + + dom = test_ctx->dom_list; + for (c = 0; c < mis; c++) { + assert_non_null(dom); + dom = dom->next; + } + assert_non_null(dom); + sss_domain_set_state(dom, DOM_DISABLED); + disabled_dom = dom; + + obj_name = talloc_asprintf(global_talloc_context, "myname@%s", + disabled_dom->name); + assert_non_null(obj_name); + + + dom = find_domain_by_object_name(test_ctx->dom_list, obj_name); + assert_null(dom); + + dom = find_domain_by_object_name_ex(test_ctx->dom_list, obj_name, true, + SSS_GND_DESCEND); + assert_null(dom); + + dom = find_domain_by_object_name_ex(test_ctx->dom_list, obj_name, true, + SSS_GND_DESCEND | SSS_GND_INCLUDE_DISABLED); + assert_non_null(dom); + assert_ptr_equal(disabled_dom, dom); + + dom = find_domain_by_object_name_ex(test_ctx->dom_list, obj_name, true, + SSS_GND_ALL_DOMAINS); + assert_non_null(dom); + assert_ptr_equal(disabled_dom, dom); + + talloc_free(obj_name); +} + +void test_find_domain_by_sid_null(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + + dom = find_domain_by_sid(NULL, NULL); + assert_null(dom); + + dom = find_domain_by_sid(test_ctx->dom_list, NULL); + assert_null(dom); + + dom = find_domain_by_sid(NULL, "S-1-5-21-1-2-3"); + assert_null(dom); +} + +void test_find_domain_by_sid(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + size_t c; + char *name; + char *flat_name; + char *sid; + + for (c = 0; c < test_ctx->dom_count; c++) { + name = talloc_asprintf(global_talloc_context, DOMNAME_TMPL, c); + assert_non_null(name); + + flat_name = talloc_asprintf(global_talloc_context, FLATNAME_TMPL, c); + assert_non_null(flat_name); + + sid = talloc_asprintf(global_talloc_context, SID_TMPL, c); + assert_non_null(sid); + + dom = find_domain_by_sid(test_ctx->dom_list, sid); + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + + talloc_free(name); + talloc_free(flat_name); + talloc_free(sid); + } +} + +void test_find_domain_by_sid_missing_sid(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + size_t c; + char *name; + char *flat_name; + char *sid; + size_t mis; + + mis = test_ctx->dom_count/2; + assert_true((mis >= 1 && mis < test_ctx->dom_count)); + + dom = test_ctx->dom_list; + for (c = 0; c < mis; c++) { + assert_non_null(dom); + dom = dom->next; + } + assert_non_null(dom); + dom->domain_id = NULL; + + for (c = 0; c < test_ctx->dom_count; c++) { + name = talloc_asprintf(global_talloc_context, DOMNAME_TMPL, c); + assert_non_null(name); + + flat_name = talloc_asprintf(global_talloc_context, FLATNAME_TMPL, c); + assert_non_null(flat_name); + + sid = talloc_asprintf(global_talloc_context, SID_TMPL, c); + assert_non_null(sid); + + dom = find_domain_by_sid(test_ctx->dom_list, sid); + if (c == mis - 1) { + assert_null(dom); + } else { + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + } + + talloc_free(name); + talloc_free(flat_name); + talloc_free(sid); + } +} + +void test_find_domain_by_sid_disabled(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom; + size_t c; + char *name; + char *flat_name; + char *sid; + size_t mis; + + mis = test_ctx->dom_count/2; + assert_true((mis >= 1 && mis < test_ctx->dom_count)); + + dom = test_ctx->dom_list; + for (c = 0; c < mis; c++) { + assert_non_null(dom); + dom = dom->next; + } + assert_non_null(dom); + sss_domain_set_state(dom, DOM_DISABLED); + + for (c = 0; c < test_ctx->dom_count; c++) { + name = talloc_asprintf(global_talloc_context, DOMNAME_TMPL, c); + assert_non_null(name); + + flat_name = talloc_asprintf(global_talloc_context, FLATNAME_TMPL, c); + assert_non_null(flat_name); + + sid = talloc_asprintf(global_talloc_context, SID_TMPL, c); + assert_non_null(sid); + + dom = find_domain_by_sid(test_ctx->dom_list, sid); + if (c == mis - 1) { + assert_null(dom); + } else { + assert_non_null(dom); + assert_string_equal(name, dom->name); + assert_string_equal(flat_name, dom->flat_name); + assert_string_equal(sid, dom->domain_id); + } + + talloc_free(name); + talloc_free(flat_name); + talloc_free(sid); + } +} + +/* + * dom1 -> sub1a + * | + * dom2 -> sub2a -> sub2b + * + */ +static int setup_dom_tree(void **state) +{ + struct dom_list_test_ctx *test_ctx; + struct sss_domain_info *head = NULL; + struct sss_domain_info *dom = NULL; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct dom_list_test_ctx); + assert_non_null(test_ctx); + + dom = named_domain(test_ctx, "dom1", NULL); + assert_non_null(dom); + head = dom; + + dom = named_domain(test_ctx, "sub1a", head); + assert_non_null(dom); + head->subdomains = dom; + + dom = named_domain(test_ctx, "dom2", NULL); + assert_non_null(dom); + head->next = dom; + + dom = named_domain(test_ctx, "sub2a", head->next); + assert_non_null(dom); + head->next->subdomains = dom; + + dom = named_domain(test_ctx, "sub2b", head->next); + assert_non_null(dom); + head->next->subdomains->next = dom; + + test_ctx->dom_count = 2; + test_ctx->dom_list = head; + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int teardown_dom_tree(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + if (test_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return 1; + } + + assert_true(check_leaks_pop(test_ctx)); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void test_get_next_domain(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom = NULL; + + dom = get_next_domain(test_ctx->dom_list, 0); + assert_non_null(dom); + assert_string_equal(dom->name, "dom2"); + + dom = get_next_domain(dom, 0); + assert_null(dom); +} + +static void test_get_next_domain_descend(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom = NULL; + + dom = get_next_domain(test_ctx->dom_list, SSS_GND_DESCEND); + assert_non_null(dom); + assert_string_equal(dom->name, "sub1a"); + + dom = get_next_domain(dom, SSS_GND_DESCEND); + assert_non_null(dom); + assert_string_equal(dom->name, "dom2"); + + dom = get_next_domain(dom, SSS_GND_DESCEND); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2a"); + + dom = get_next_domain(dom, SSS_GND_DESCEND); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2b"); + + dom = get_next_domain(dom, 0); + assert_null(dom); +} + +static void test_get_next_domain_disabled(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom = NULL; + + for (dom = test_ctx->dom_list; dom; + dom = get_next_domain(dom, SSS_GND_DESCEND)) { + sss_domain_set_state(dom, DOM_DISABLED); + } + + dom = get_next_domain(test_ctx->dom_list, SSS_GND_DESCEND); + assert_null(dom); +} + +static void test_get_next_domain_flags(void **state) +{ + struct dom_list_test_ctx *test_ctx = talloc_get_type(*state, + struct dom_list_test_ctx); + struct sss_domain_info *dom = NULL; + uint32_t gnd_flags; + + /* No flags; all doms enabled */ + gnd_flags = 0; + + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "dom2"); + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); + + /* Descend flag only; all doms enabled */ + gnd_flags = SSS_GND_DESCEND; + + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub1a"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "dom2"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2a"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2b"); + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); + + /* Incl. disabled flag only; all doms enabled */ + gnd_flags = SSS_GND_INCLUDE_DISABLED; + + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "dom2"); + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); + + /* Descend and include disabled; all doms enabled */ + gnd_flags = SSS_GND_DESCEND | SSS_GND_INCLUDE_DISABLED; + + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub1a"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "dom2"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2a"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2b"); + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); + + /* Now disable dom2 and sub2a */ + dom = find_domain_by_name(test_ctx->dom_list, "dom2", false); + assert_non_null(dom); + sss_domain_set_state(dom, DOM_DISABLED); + + dom = find_domain_by_name(test_ctx->dom_list, "sub2a", false); + assert_non_null(dom); + sss_domain_set_state(dom, DOM_DISABLED); + + /* No flags; dom2 and sub2a disabled */ + gnd_flags = 0; + + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_null(dom); + + /* Descend flag only; dom2 and sub2a disabled */ + gnd_flags = SSS_GND_DESCEND; + + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub1a"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2b"); + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); + + /* Incl. disabled flag only; dom2 and sub2a disabled */ + gnd_flags = SSS_GND_INCLUDE_DISABLED; + + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "dom2"); + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); + + /* Descend and include disabled; dom2 and sub2a disabled */ + gnd_flags = SSS_GND_DESCEND | SSS_GND_INCLUDE_DISABLED; + + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub1a"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "dom2"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2a"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2b"); + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); + + /* Descend only to subdomains */ + gnd_flags = SSS_GND_SUBDOMAINS | SSS_GND_INCLUDE_DISABLED; + + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub1a"); + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); + + dom = find_domain_by_name_ex(test_ctx->dom_list, "dom2", true, + SSS_GND_ALL_DOMAINS); + assert_non_null(dom); + assert_string_equal(dom->name, "dom2"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2a"); + + dom = get_next_domain(dom, gnd_flags); + assert_non_null(dom); + assert_string_equal(dom->name, "sub2b"); + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); + + /* Expect NULL if the domain has no sub-domains */ + test_ctx->dom_list->subdomains = NULL; + dom = get_next_domain(test_ctx->dom_list, gnd_flags); + assert_null(dom); +} + +struct name_init_test_ctx { + struct confdb_ctx *confdb; +}; + +#define GLOBAL_FULL_NAME_FORMAT "%1$s@%2$s" +#define TEST_DOMAIN_NAME_LDAP "test.dom" +#define TEST_DOMAIN_NAME_IPA "test.ipa" +#define TEST_DOMAIN_NAMES TEST_DOMAIN_NAME_LDAP "," TEST_DOMAIN_NAME_IPA +#define DOMAIN_FULL_NAME_FORMAT "%3$s\\%1$s" +#define DOMAIN_RE_EXPRESSION "(((?P<name>[^@]+)@(?P<domain>.+$))|" \ + "(^(?P<name>[^@\\\\]+)$))" + +static int confdb_test_setup(void **state) +{ + struct name_init_test_ctx *test_ctx; + char *conf_db = NULL; + char *dompath = NULL; + int ret; + const char *val[2]; + val[1] = NULL; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct name_init_test_ctx); + assert_non_null(test_ctx); + + conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_DB); + assert_non_null(conf_db); + + ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db); + assert_int_equal(ret, EOK); + + talloc_free(conf_db); + + val[0] = TEST_DOMAIN_NAMES; + ret = confdb_add_param(test_ctx->confdb, true, + "config/sssd", "domains", val); + assert_int_equal(ret, EOK); + + val[0] = GLOBAL_FULL_NAME_FORMAT; + ret = confdb_add_param(test_ctx->confdb, true, + "config/sssd", "full_name_format", val); + assert_int_equal(ret, EOK); + + val[0] = SSS_DEFAULT_RE; + ret = confdb_add_param(test_ctx->confdb, true, + "config/sssd", "re_expression", val); + assert_int_equal(ret, EOK); + + dompath = talloc_asprintf(test_ctx, "config/domain/%s", TEST_DOMAIN_NAME_LDAP); + assert_non_null(dompath); + + val[0] = "ldap"; + ret = confdb_add_param(test_ctx->confdb, true, + dompath, "id_provider", val); + assert_int_equal(ret, EOK); + + val[0] = DOMAIN_FULL_NAME_FORMAT; + ret = confdb_add_param(test_ctx->confdb, true, + dompath, "full_name_format", val); + assert_int_equal(ret, EOK); + + val[0] = DOMAIN_RE_EXPRESSION; + ret = confdb_add_param(test_ctx->confdb, true, + dompath, "re_expression", val); + assert_int_equal(ret, EOK); + + talloc_free(dompath); + + dompath = talloc_asprintf(test_ctx, "config/domain/%s", TEST_DOMAIN_NAME_IPA); + assert_non_null(dompath); + + val[0] = "ipa"; + ret = confdb_add_param(test_ctx->confdb, true, + dompath, "id_provider", val); + assert_int_equal(ret, EOK); + + val[0] = DOMAIN_FULL_NAME_FORMAT; + ret = confdb_add_param(test_ctx->confdb, true, + dompath, "full_name_format", val); + assert_int_equal(ret, EOK); + + val[0] = SSS_IPA_AD_DEFAULT_RE; + ret = confdb_add_param(test_ctx->confdb, true, + dompath, "re_expression", val); + assert_int_equal(ret, EOK); + + talloc_free(dompath); + + check_leaks_push(test_ctx); + *state = test_ctx; + return 0; +} + +static int confdb_test_teardown(void **state) +{ + struct name_init_test_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct name_init_test_ctx); + + assert_true(check_leaks_pop(test_ctx) == true); + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void test_sss_names_init(void **state) +{ + struct name_init_test_ctx *test_ctx; + struct sss_names_ctx *names_ctx; + int ret; + + test_ctx = talloc_get_type(*state, struct name_init_test_ctx); + + ret = sss_names_init(test_ctx, test_ctx->confdb, NULL, &names_ctx); + assert_int_equal(ret, EOK); + assert_non_null(names_ctx); + assert_string_equal(names_ctx->re_pattern, SSS_DEFAULT_RE); + assert_string_equal(names_ctx->fq_fmt, GLOBAL_FULL_NAME_FORMAT); + + talloc_free(names_ctx); + + ret = sss_names_init(test_ctx, test_ctx->confdb, TEST_DOMAIN_NAME_LDAP, + &names_ctx); + assert_int_equal(ret, EOK); + assert_non_null(names_ctx); + assert_string_equal(names_ctx->re_pattern, DOMAIN_RE_EXPRESSION); + assert_string_equal(names_ctx->fq_fmt, DOMAIN_FULL_NAME_FORMAT); + + talloc_free(names_ctx); +} + +void test_sss_names_ipa_ad_regexp(void **state) +{ + struct name_init_test_ctx *test_ctx; + struct sss_names_ctx *names_ctx; + char *name; + char *domain; + int ret; + + test_ctx = talloc_get_type(*state, struct name_init_test_ctx); + + ret = sss_names_init(test_ctx, test_ctx->confdb, TEST_DOMAIN_NAME_IPA, + &names_ctx); + assert_int_equal(ret, EOK); + assert_non_null(names_ctx); + assert_non_null(names_ctx->re_pattern); + + ret = sss_parse_name(names_ctx, names_ctx, "user@domain", &domain, &name); + assert_int_equal(ret, EOK); + assert_string_equal(name, "user"); + assert_string_equal(domain, "domain"); + talloc_free(name); + talloc_free(domain); + + ret = sss_parse_name(names_ctx, names_ctx, "mail@group@domain", &domain, &name); + assert_int_equal(ret, EOK); + assert_string_equal(name, "mail@group"); + assert_string_equal(domain, "domain"); + talloc_free(name); + talloc_free(domain); + + ret = sss_parse_name(names_ctx, names_ctx, "domain\\user", &domain, &name); + assert_int_equal(ret, EOK); + assert_string_equal(name, "user"); + assert_string_equal(domain, "domain"); + talloc_free(name); + talloc_free(domain); + + ret = sss_parse_name(names_ctx, names_ctx, "user", &domain, &name); + assert_int_equal(ret, EOK); + assert_string_equal(name, "user"); + assert_null(domain); + talloc_free(name); + + talloc_free(names_ctx); +} + +void test_well_known_sid_to_name(void **state) +{ + int ret; + const char *name; + const char *dom; + + ret = well_known_sid_to_name(NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("abc", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-0", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-0-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-0-0", &dom, &name); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "NULL AUTHORITY"); + assert_string_equal(name, "NULL SID"); + + ret = well_known_sid_to_name("S-1-0-0-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-3", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-3-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-3-4", &dom, &name); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "CREATOR AUTHORITY"); + assert_string_equal(name, "OWNER RIGHTS"); + + ret = well_known_sid_to_name("S-1-16", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-16-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-16-8192", &dom, &name); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "MANDATORY LABEL AUTHORITY"); + assert_string_equal(name, "MEDIUM"); + + ret = well_known_sid_to_name("S-1-18", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-18-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-18-1", &dom, &name); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "AUTHENTICATION AUTHORITY"); + assert_string_equal(name, "AUTHENTICATION ASSERTION"); + + ret = well_known_sid_to_name("S-1-5", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-5", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-5-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-5-7", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-5-7-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-5-7-8-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-5-7-8", &dom, &name); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "NT AUTHORITY"); + assert_string_equal(name, "LOGON ID"); + + ret = well_known_sid_to_name("S-1-5-6", &dom, &name); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "NT AUTHORITY"); + assert_string_equal(name, "SERVICE"); + + ret = well_known_sid_to_name("S-1-5-6-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-21", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-21-", &dom, &name); + assert_int_equal(ret, ENOENT); + + ret = well_known_sid_to_name("S-1-5-21-abc", &dom, &name); + assert_int_equal(ret, ENOENT); + + ret = well_known_sid_to_name("S-1-5-32", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-32-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-32-551", &dom, &name); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "BUILTIN"); + assert_string_equal(name, "Backup Operators"); + + ret = well_known_sid_to_name("S-1-5-32-551-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-64", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-64-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-64-10", &dom, &name); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "NT AUTHORITY"); + assert_string_equal(name, "NTLM AUTHENTICATION"); + + ret = well_known_sid_to_name("S-1-5-64-10-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-65", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-65-", &dom, &name); + assert_int_equal(ret, EINVAL); + + ret = well_known_sid_to_name("S-1-5-65-1", &dom, &name); + assert_int_equal(ret, EOK); + assert_string_equal(dom, "NT AUTHORITY"); + assert_string_equal(name, "THIS ORGANIZATION CERTIFICATE"); + + ret = well_known_sid_to_name("S-1-5-65-1-", &dom, &name); + assert_int_equal(ret, EINVAL); +} + +void test_name_to_well_known_sid(void **state) +{ + int ret; + const char *sid; + + ret = name_to_well_known_sid(NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + ret = name_to_well_known_sid("abc", "def", &sid); + assert_int_equal(ret, ENOENT); + + ret = name_to_well_known_sid("", "def", &sid); + assert_int_equal(ret, ENOENT); + + ret = name_to_well_known_sid("BUILTIN", "def", &sid); + assert_int_equal(ret, EINVAL); + + ret = name_to_well_known_sid("NT AUTHORITY", "def", &sid); + assert_int_equal(ret, EINVAL); + + ret = name_to_well_known_sid("LOCAL AUTHORITY", "LOCAL", &sid); + assert_int_equal(ret, EOK); + assert_string_equal(sid, "S-1-2-0"); + + ret = name_to_well_known_sid(NULL, "LOCAL", &sid); + assert_int_equal(ret, EINVAL); + + ret = name_to_well_known_sid("BUILTIN", "Cryptographic Operators", &sid); + assert_int_equal(ret, EOK); + assert_string_equal(sid, "S-1-5-32-569"); + + ret = name_to_well_known_sid("NT AUTHORITY", "DIALUP", &sid); + assert_int_equal(ret, EOK); + assert_string_equal(sid, "S-1-5-1"); + + ret = name_to_well_known_sid("NT AUTHORITY", "NTLM AUTHENTICATION", &sid); + assert_int_equal(ret, EOK); + assert_string_equal(sid, "S-1-5-64-10"); + + ret = name_to_well_known_sid("NT AUTHORITY", "THIS ORGANIZATION CERTIFICATE", &sid); + assert_int_equal(ret, EOK); + assert_string_equal(sid, "S-1-5-65-1"); + + ret = name_to_well_known_sid("NT AUTHORITY", "LOGON ID", &sid); + assert_int_equal(ret, EOK); + assert_string_equal(sid, "S-1-5-5-0-0"); + + ret = name_to_well_known_sid("MANDATORY LABEL AUTHORITY", "MEDIUM", &sid); + assert_int_equal(ret, EOK); + assert_string_equal(sid, "S-1-16-8192"); + + ret = name_to_well_known_sid("AUTHENTICATION AUTHORITY", "KEY_TRUST_IDENTITY", &sid); + assert_int_equal(ret, EOK); + assert_string_equal(sid, "S-1-18-4"); +} + +#define TEST_SANITIZE_INPUT "TestUser@Test.Domain" +#define TEST_SANITIZE_LC_INPUT "testuser@test.domain" + +void test_sss_filter_sanitize_for_dom(void **state) +{ + struct dom_list_test_ctx *test_ctx; + int ret; + char *sanitized; + char *lc_sanitized; + struct sss_domain_info *dom; + + test_ctx = talloc_get_type(*state, struct dom_list_test_ctx); + dom = test_ctx->dom_list; + + dom->case_sensitive = true; + + ret = sss_filter_sanitize_for_dom(test_ctx, TEST_SANITIZE_INPUT, dom, + &sanitized, &lc_sanitized); + assert_int_equal(ret, EOK); + assert_string_equal(sanitized, TEST_SANITIZE_INPUT); + assert_string_equal(lc_sanitized, TEST_SANITIZE_INPUT); + talloc_free(sanitized); + talloc_free(lc_sanitized); + + dom->case_sensitive = false; + + ret = sss_filter_sanitize_for_dom(test_ctx, TEST_SANITIZE_INPUT, dom, + &sanitized, &lc_sanitized); + assert_int_equal(ret, EOK); + assert_string_equal(sanitized, TEST_SANITIZE_INPUT); + assert_string_equal(lc_sanitized, TEST_SANITIZE_LC_INPUT); + talloc_free(sanitized); + talloc_free(lc_sanitized); +} + +void check_expanded_value(TALLOC_CTX *tmp_ctx, + struct sss_nss_homedir_ctx *homedir_ctx, + const char *template, const char *exp_val) +{ + char *homedir; + + homedir = expand_homedir_template(tmp_ctx, template, false, homedir_ctx); + if (exp_val != NULL) { + assert_string_equal(homedir, exp_val); + } else { + assert_null(homedir); + } + + talloc_free(homedir); +} + +static int setup_homedir_ctx(void **state) +{ + struct sss_nss_homedir_ctx *homedir_ctx; + + assert_true(leak_check_setup()); + + homedir_ctx= talloc_zero(global_talloc_context, + struct sss_nss_homedir_ctx); + assert_non_null(homedir_ctx); + + homedir_ctx->username = sss_create_internal_fqname(homedir_ctx, + USERNAME, DOMAIN); + if (homedir_ctx->username == NULL) { + talloc_free(homedir_ctx); + return 1; + } + + homedir_ctx->uid = UID; + homedir_ctx->original = ORIGINAL_HOME; + homedir_ctx->domain = DOMAIN; + homedir_ctx->flatname = FLATNAME; + homedir_ctx->config_homedir_substr = HOMEDIR_SUBSTR; + + check_leaks_push(homedir_ctx); + *state = homedir_ctx; + return 0; +} + +static int teardown_homedir_ctx(void **state) +{ + struct sss_nss_homedir_ctx *homedir_ctx = talloc_get_type(*state, + struct sss_nss_homedir_ctx); + if (homedir_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Type mismatch\n"); + return 1; + } + + assert_true(check_leaks_pop(homedir_ctx) == true); + talloc_free(homedir_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +void test_expand_homedir_template_NULL(void **state) +{ + TALLOC_CTX *tmp_ctx; + char *homedir; + struct sss_nss_homedir_ctx *homedir_ctx; + + /* following format strings requires data in homedir_ctx */ + const char *format_strings[] = { "%u", "%U", "%d", "%f", "%F", "%H", + NULL }; + int i; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + + homedir_ctx = talloc_zero(tmp_ctx, struct sss_nss_homedir_ctx); + assert_non_null(homedir_ctx); + + homedir = expand_homedir_template(tmp_ctx, NULL, false, NULL); + assert_null(homedir); + + homedir = expand_homedir_template(tmp_ctx, "template", false, NULL); + assert_null(homedir); + + /* missing data in homedir_ctx */ + check_expanded_value(tmp_ctx, homedir_ctx, "%%", "%"); + check_expanded_value(tmp_ctx, homedir_ctx, "%o", ""); + + for (i = 0; format_strings[i] != NULL; ++i) { + check_expanded_value(tmp_ctx, homedir_ctx, format_strings[i], NULL); + } + + /* flatname requires domain and username */ + homedir_ctx->username = DUMMY; + check_expanded_value(tmp_ctx, homedir_ctx, "%f", NULL); + + homedir_ctx->username = NULL; + homedir_ctx->domain = DUMMY; + check_expanded_value(tmp_ctx, homedir_ctx, "%f", NULL); + + /* test unknown format string */ + check_expanded_value(tmp_ctx, homedir_ctx, "%x", NULL); + + /* test malformed format string */ + check_expanded_value(tmp_ctx, homedir_ctx, "%", NULL); + + talloc_free(tmp_ctx); +} + +void test_expand_homedir_template(void **state) +{ + struct sss_nss_homedir_ctx *homedir_ctx = talloc_get_type(*state, + struct sss_nss_homedir_ctx); + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + + /* string without template */ + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY, DUMMY); + + check_expanded_value(tmp_ctx, homedir_ctx, "%u", USERNAME); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%u", DUMMY USERNAME); + check_expanded_value(tmp_ctx, homedir_ctx, "%u"DUMMY, USERNAME DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%u"DUMMY2, + DUMMY USERNAME DUMMY2); + + check_expanded_value(tmp_ctx, homedir_ctx, "%U", STR(UID)); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%U", DUMMY STR(UID)); + check_expanded_value(tmp_ctx, homedir_ctx, "%U"DUMMY, STR(UID) DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%U"DUMMY2, + DUMMY STR(UID) DUMMY2); + + check_expanded_value(tmp_ctx, homedir_ctx, "%d", DOMAIN); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%d", DUMMY DOMAIN); + check_expanded_value(tmp_ctx, homedir_ctx, "%d"DUMMY, DOMAIN DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%d"DUMMY2, + DUMMY DOMAIN DUMMY2); + + check_expanded_value(tmp_ctx, homedir_ctx, "%f", USERNAME"@"DOMAIN); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%f", + DUMMY USERNAME"@"DOMAIN); + check_expanded_value(tmp_ctx, homedir_ctx, "%f"DUMMY, + USERNAME"@"DOMAIN DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%f"DUMMY2, + DUMMY USERNAME"@"DOMAIN DUMMY2); + + check_expanded_value(tmp_ctx, homedir_ctx, "%o", ORIGINAL_HOME); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%o", DUMMY ORIGINAL_HOME); + check_expanded_value(tmp_ctx, homedir_ctx, "%o"DUMMY, ORIGINAL_HOME DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%o"DUMMY2, + DUMMY ORIGINAL_HOME DUMMY2); + + check_expanded_value(tmp_ctx, homedir_ctx, "%h", LOWERCASE_HOME); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%h", DUMMY LOWERCASE_HOME); + check_expanded_value(tmp_ctx, homedir_ctx, "%h"DUMMY, LOWERCASE_HOME DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%h"DUMMY2, + DUMMY LOWERCASE_HOME DUMMY2); + + check_expanded_value(tmp_ctx, homedir_ctx, "%F", FLATNAME); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%F", DUMMY FLATNAME); + check_expanded_value(tmp_ctx, homedir_ctx, "%F"DUMMY, FLATNAME DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%F"DUMMY2, + DUMMY FLATNAME DUMMY2); + + check_expanded_value(tmp_ctx, homedir_ctx, "%H", HOMEDIR_SUBSTR); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%H", + DUMMY HOMEDIR_SUBSTR); + check_expanded_value(tmp_ctx, homedir_ctx, "%H"DUMMY, + HOMEDIR_SUBSTR DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%H"DUMMY2, + DUMMY HOMEDIR_SUBSTR DUMMY2); + + check_expanded_value(tmp_ctx, homedir_ctx, "%%", "%"); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%%", DUMMY"%"); + check_expanded_value(tmp_ctx, homedir_ctx, "%%"DUMMY, "%"DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%%"DUMMY2, + DUMMY"%"DUMMY2); + + check_expanded_value(tmp_ctx, homedir_ctx, "%l", FIRST_LETTER); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%l", DUMMY FIRST_LETTER); + check_expanded_value(tmp_ctx, homedir_ctx, "%l"DUMMY, FIRST_LETTER DUMMY); + check_expanded_value(tmp_ctx, homedir_ctx, DUMMY"%l"DUMMY2, + DUMMY FIRST_LETTER DUMMY2); + + /* test all format strings */ + check_expanded_value(tmp_ctx, homedir_ctx, + DUMMY"/%u/%U/%d/%f/%o/%F/%%/%H/%l/"DUMMY2, + DUMMY"/"USERNAME"/" STR(UID) "/"DOMAIN"/" + USERNAME"@"DOMAIN"/"ORIGINAL_HOME"/"FLATNAME"/%/" + HOMEDIR_SUBSTR"/"FIRST_LETTER"/"DUMMY2); + talloc_free(tmp_ctx); +} + +static int setup_leak_tests(void **state) +{ + assert_true(leak_check_setup()); + + return 0; +} + +static int teardown_leak_tests(void **state) +{ + assert_true(leak_check_teardown()); + return 0; +} + +/* add_strings_list() is an alias for add_strings_list_ex() that allows for + * duplicate values. + */ +void test_add_strings_lists(void **state) +{ + const char *l1[] = {"a", "b", "c", "b", NULL}; + const char *l2[] = {"1", "2", "3", "2", NULL}; + const char **res; + int ret; + size_t c; + size_t d; + + ret = add_strings_lists(global_talloc_context, NULL, NULL, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + assert_null(res[0]); + talloc_free(res); + + ret = add_strings_lists(global_talloc_context, NULL, NULL, false, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + assert_null(res[0]); + talloc_free(res); + + ret = add_strings_lists(global_talloc_context, l1, NULL, false, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; l1[c] != NULL; c++) { + /* 'copy_strings' is 'false', pointers must be equal */ + assert_int_equal(memcmp(&l1[c], &res[c], sizeof(char *)), 0); + } + assert_null(res[c]); + talloc_free(res); + + ret = add_strings_lists(global_talloc_context, l1, NULL, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; l1[c] != NULL; c++) { + /* 'copy_strings' is 'true', pointers must be different, but strings + * must be equal */ + assert_int_not_equal(memcmp(&l1[c], &res[c], sizeof(char *)), 0); + assert_string_equal(l1[c], res[c]); + } + assert_null(res[c]); + talloc_free(res); + + ret = add_strings_lists(global_talloc_context, NULL, l1, false, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; l1[c] != NULL; c++) { + /* 'copy_strings' is 'false', pointers must be equal */ + assert_int_equal(memcmp(&l1[c], &res[c], sizeof(char *)), 0); + } + assert_null(res[c]); + talloc_free(res); + + ret = add_strings_lists(global_talloc_context, NULL, l1, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; l1[c] != NULL; c++) { + /* 'copy_strings' is 'true', pointers must be different, but strings + * must be equal */ + assert_int_not_equal(memcmp(&l1[c], &res[c], sizeof(char *)), 0); + assert_string_equal(l1[c], res[c]); + } + assert_null(res[c]); + talloc_free(res); + + ret = add_strings_lists(global_talloc_context, l1, l2, false, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; l1[c] != NULL; c++) { + /* 'copy_strings' is 'false', pointers must be equal */ + assert_int_equal(memcmp(&l1[c], &res[c], sizeof(char *)), 0); + } + for (d = 0; l2[d] != NULL; d++) { + assert_int_equal(memcmp(&l2[d], &res[c+d], sizeof(char *)), 0); + } + assert_null(res[c+d]); + talloc_free(res); + + ret = add_strings_lists(global_talloc_context, l1, l2, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; l1[c] != NULL; c++) { + /* 'copy_strings' is 'true', pointers must be different, but strings + * must be equal */ + assert_int_not_equal(memcmp(&l1[c], &res[c], sizeof(char *)), 0); + assert_string_equal(l1[c], res[c]); + } + for (d = 0; l2[d] != NULL; d++) { + assert_int_not_equal(memcmp(&l2[d], &res[c+d], sizeof(char *)), 0); + assert_string_equal(l2[d], res[c+d]); + } + assert_null(res[c+d]); + talloc_free(res); +} + +/* add_strings_list_ex(skip_dups=false) was tested by add_string_list(). + * We now test add_strings_list_ex(skip_dups=true). + */ +void test_add_strings_lists_ex(void **state) +{ + /* Set duplicate values at the end of the array to simplify the comparison */ + const char *l1[] = {"a", "b", "c", "b", NULL}; + const char *l2[] = {"1", "2", "3", "2", NULL}; + const char *r1[sizeof(l1) / sizeof(*l1) - 1]; + const char *r2[sizeof(l2) / sizeof(*l2) - 1]; + const char **res; + int ret; + size_t c; + size_t d; + + /* The expected results must have the same pointers */ + memcpy(r1, l1, sizeof(r1) - sizeof(*r1)); + r1[sizeof(r1) / sizeof(*r1) - 1] = NULL; + memcpy(r2, l2, sizeof(r2) - sizeof(*r2)); + r2[sizeof(r2) / sizeof(*r2) - 1] = NULL; + + ret = add_strings_lists_ex(global_talloc_context, NULL, NULL, true, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + assert_null(res[0]); + talloc_free(res); + + ret = add_strings_lists_ex(global_talloc_context, NULL, NULL, false, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + assert_null(res[0]); + talloc_free(res); + + ret = add_strings_lists_ex(global_talloc_context, l1, NULL, false, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; r1[c] != NULL; c++) { + /* 'copy_strings' is 'false', pointers must be equal */ + assert_int_equal(memcmp(&r1[c], &res[c], sizeof(char *)), 0); + } + assert_null(res[c]); + talloc_free(res); + + ret = add_strings_lists_ex(global_talloc_context, l1, NULL, true, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; r1[c] != NULL; c++) { + /* 'copy_strings' is 'true', pointers must be different, but strings + * must be equal */ + assert_int_not_equal(memcmp(&r1[c], &res[c], sizeof(char *)), 0); + assert_string_equal(r1[c], res[c]); + } + assert_null(res[c]); + talloc_free(res); + + ret = add_strings_lists_ex(global_talloc_context, NULL, l1, false, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; r1[c] != NULL; c++) { + /* 'copy_strings' is 'false', pointers must be equal */ + assert_int_equal(memcmp(&r1[c], &res[c], sizeof(char *)), 0); + } + assert_null(res[c]); + talloc_free(res); + + ret = add_strings_lists_ex(global_talloc_context, NULL, l1, true, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; r1[c] != NULL; c++) { + /* 'copy_strings' is 'true', pointers must be different, but strings + * must be equal */ + assert_int_not_equal(memcmp(&r1[c], &res[c], sizeof(char *)), 0); + assert_string_equal(r1[c], res[c]); + } + assert_null(res[c]); + talloc_free(res); + + ret = add_strings_lists_ex(global_talloc_context, l1, l2, false, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; r1[c] != NULL; c++) { + /* 'copy_strings' is 'false', pointers must be equal */ + assert_int_equal(memcmp(&r1[c], &res[c], sizeof(char *)), 0); + } + for (d = 0; r2[d] != NULL; d++) { + assert_int_equal(memcmp(&r2[d], &res[c+d], sizeof(char *)), 0); + } + assert_null(res[c+d]); + talloc_free(res); + + ret = add_strings_lists_ex(global_talloc_context, l1, l2, true, true, &res); + assert_int_equal(ret, EOK); + assert_non_null(res); + for (c = 0; r1[c] != NULL; c++) { + /* 'copy_strings' is 'true', pointers must be different, but strings + * must be equal */ + assert_int_not_equal(memcmp(&r1[c], &res[c], sizeof(char *)), 0); + assert_string_equal(r1[c], res[c]); + } + for (d = 0; r2[d] != NULL; d++) { + assert_int_not_equal(memcmp(&r2[d], &res[c+d], sizeof(char *)), 0); + assert_string_equal(r2[d], res[c+d]); + } + assert_null(res[c+d]); + talloc_free(res); +} + +void test_sss_write_krb5_conf_snippet(void **state) +{ + int ret; + char buf[PATH_MAX]; + char *cwd; + char *path; + char *file; + char *file_krb5_libdefaults; + + ret = sss_write_krb5_conf_snippet(NULL, false, false); + assert_int_equal(ret, EINVAL); + + ret = sss_write_krb5_conf_snippet("abc", false, false); + assert_int_equal(ret, EINVAL); + + ret = sss_write_krb5_conf_snippet("", false, false); + assert_int_equal(ret, EOK); + + ret = sss_write_krb5_conf_snippet("none", false, false); + assert_int_equal(ret, EOK); + + cwd = getcwd(buf, PATH_MAX); + assert_non_null(cwd); + + ret = asprintf(&path, "%s/%s", cwd, TESTS_PATH); + assert_true(ret > 0); + + ret = asprintf(&file, "%s/%s/localauth_plugin", cwd, TESTS_PATH); + assert_true(ret > 0); + + ret = asprintf(&file_krb5_libdefaults, + "%s/%s/krb5_libdefaults", cwd, TESTS_PATH); + assert_true(ret > 0); + + ret = sss_write_krb5_conf_snippet(path, true, true); + assert_int_equal(ret, EOK); + + /* Check if writing a second time will work as well */ + ret = sss_write_krb5_conf_snippet(path, true, true); + assert_int_equal(ret, EOK); + +#ifdef HAVE_KRB5_LOCALAUTH_PLUGIN + ret = unlink(file); + assert_int_equal(ret, EOK); +#endif + + ret = unlink(file_krb5_libdefaults); + assert_int_equal(ret, EOK); + + free(file); + free(file_krb5_libdefaults); + free(path); +} + +void test_get_hidden_path(void **state) +{ + char *s; + + assert_null(get_hidden_tmp_path(NULL, NULL)); + assert_null(get_hidden_tmp_path(NULL, "/")); + assert_null(get_hidden_tmp_path(NULL, "/abc/")); + + s = get_hidden_tmp_path(NULL, "abc"); + assert_string_equal(s, ".abcXXXXXX"); + talloc_free(s); + + s = get_hidden_tmp_path(NULL, "/abc"); + assert_string_equal(s, "/.abcXXXXXX"); + talloc_free(s); + + s = get_hidden_tmp_path(NULL, "/xyz/xyz/xyz//abc"); + assert_string_equal(s, "/xyz/xyz/xyz//.abcXXXXXX"); + talloc_free(s); +} + +struct unique_file_test_ctx { + char *filename; +}; + +static int unique_file_test_setup(void **state) +{ + struct unique_file_test_ctx *test_ctx; + + assert_true(leak_check_setup()); + + test_ctx = talloc_zero(global_talloc_context, struct unique_file_test_ctx); + assert_non_null(test_ctx); + + test_ctx->filename = talloc_strdup(test_ctx, "test_unique_file_XXXXXX"); + assert_non_null(test_ctx); + + *state = test_ctx; + return 0; +} + +static int unique_file_test_teardown(void **state) +{ + struct unique_file_test_ctx *test_ctx; + errno_t ret; + + test_ctx = talloc_get_type(*state, struct unique_file_test_ctx); + + errno = 0; + ret = unlink(test_ctx->filename); + if (ret != 0 && errno != ENOENT) { + fail(); + } + + talloc_free(test_ctx); + assert_true(leak_check_teardown()); + return 0; +} + +static void assert_destructor(TALLOC_CTX *owner, + struct unique_file_test_ctx *test_ctx) +{ + errno_t ret; + char *check_filename; + + /* Test that the destructor works */ + if (owner == NULL) { + return; + } + + check_filename = talloc_strdup(test_ctx, test_ctx->filename); + assert_non_null(check_filename); + + talloc_free(owner); + + ret = check_file(check_filename, geteuid(), getegid(), + (S_IRUSR | S_IWUSR | S_IFREG), 0, NULL, true); + assert_int_not_equal(ret, EOK); +} + +static void sss_unique_file_test(struct unique_file_test_ctx *test_ctx, + bool test_destructor) +{ + int fd; + errno_t ret; + TALLOC_CTX *owner = NULL; + + if (test_destructor) { + owner = talloc_new(test_ctx); + assert_non_null(owner); + } + + fd = sss_unique_file(owner, test_ctx->filename, &ret); + assert_int_not_equal(fd, -1); + assert_int_equal(ret, EOK); + + ret = check_file(test_ctx->filename, geteuid(), getegid(), + (S_IRUSR | S_IWUSR | S_IFREG), 0, NULL, false); + close(fd); + assert_int_equal(ret, EOK); + + assert_destructor(owner, test_ctx); +} + +static void test_sss_unique_file(void **state) +{ + struct unique_file_test_ctx *test_ctx; + test_ctx = talloc_get_type(*state, struct unique_file_test_ctx); + sss_unique_file_test(test_ctx, false); +} + +static void test_sss_unique_file_destruct(void **state) +{ + struct unique_file_test_ctx *test_ctx; + test_ctx = talloc_get_type(*state, struct unique_file_test_ctx); + sss_unique_file_test(test_ctx, true); +} + +static void test_sss_unique_file_neg(void **state) +{ + int fd; + errno_t ret; + + fd = sss_unique_file(NULL, discard_const("badpattern"), &ret); + assert_int_equal(fd, -1); + assert_int_equal(ret, EINVAL); +} + +static void sss_unique_filename_test(struct unique_file_test_ctx *test_ctx, + bool test_destructor) +{ + errno_t ret; + char *tmp_filename; + TALLOC_CTX *owner = NULL; + + tmp_filename = talloc_strdup(test_ctx, test_ctx->filename); + assert_non_null(tmp_filename); + + if (test_destructor) { + owner = talloc_new(test_ctx); + assert_non_null(owner); + } + + ret = sss_unique_filename(owner, test_ctx->filename); + assert_int_equal(ret, EOK); + + assert_int_equal(strncmp(test_ctx->filename, + tmp_filename, + strlen(tmp_filename) - sizeof("XXXXXX")), + 0); + + ret = check_file(test_ctx->filename, geteuid(), getegid(), + (S_IRUSR | S_IWUSR | S_IFREG), 0, NULL, true); + assert_int_equal(ret, EOK); + + assert_destructor(owner, test_ctx); +} + +static void test_sss_unique_filename(void **state) +{ + struct unique_file_test_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct unique_file_test_ctx); + sss_unique_filename_test(test_ctx, false); +} + +static void test_sss_unique_filename_destruct(void **state) +{ + struct unique_file_test_ctx *test_ctx; + + test_ctx = talloc_get_type(*state, struct unique_file_test_ctx); + sss_unique_filename_test(test_ctx, true); +} + +static void test_parse_cert_verify_opts(void **state) +{ + int ret; + struct cert_verify_opts *cv_opts; + + ret = parse_cert_verify_opts(global_talloc_context, NULL, &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_false(cv_opts->verification_partial_chain); + assert_true(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + assert_null(cv_opts->crl_files); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, "wedfkwefjk", &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_false(cv_opts->verification_partial_chain); + assert_true(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + assert_null(cv_opts->crl_files); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, "no_ocsp", &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_false(cv_opts->verification_partial_chain); + assert_false(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + assert_null(cv_opts->crl_files); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, "no_verification", + &cv_opts); + assert_int_equal(ret, EOK); + assert_false(cv_opts->do_verification); + assert_false(cv_opts->verification_partial_chain); + assert_true(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + assert_null(cv_opts->crl_files); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, + "no_ocsp,no_verification", &cv_opts); + assert_int_equal(ret, EOK); + assert_false(cv_opts->do_verification); + assert_false(cv_opts->verification_partial_chain); + assert_false(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + assert_null(cv_opts->crl_files); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, + "ocsp_default_responder=", &cv_opts); + assert_int_equal(ret, EINVAL); + + ret = parse_cert_verify_opts(global_talloc_context, + "ocsp_default_responder_signing_cert=", + &cv_opts); + assert_int_equal(ret, EINVAL); + + ret = parse_cert_verify_opts(global_talloc_context, + "ocsp_default_responder=abc," + "ocsp_default_responder_signing_cert=def", + &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_false(cv_opts->verification_partial_chain); + assert_true(cv_opts->do_ocsp); + assert_string_equal(cv_opts->ocsp_default_responder, "abc"); + assert_string_equal(cv_opts->ocsp_default_responder_signing_cert, "def"); + assert_null(cv_opts->crl_files); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, "crl_file=hij", + &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_false(cv_opts->verification_partial_chain); + assert_true(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + assert_string_equal(cv_opts->crl_files[0], "hij"); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, + "crl_file=file1.pem,crl_file=file2.pem", + &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_false(cv_opts->verification_partial_chain); + assert_true(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + assert_string_equal(cv_opts->crl_files[0], "file1.pem"); + assert_string_equal(cv_opts->crl_files[1], "file2.pem"); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, "partial_chain", &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_true(cv_opts->verification_partial_chain); + assert_true(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + assert_null(cv_opts->crl_files); + talloc_free(cv_opts); +} + +static void assert_parse_fqname(const char *fqname, + const char *exp_shortname, + const char *exp_domname) +{ + errno_t ret; + char *shortname = NULL; + char *domname = NULL; + + check_leaks_push(global_talloc_context); + + ret = sss_parse_internal_fqname(global_talloc_context, fqname, + exp_shortname ? &shortname : NULL, + exp_domname ? &domname : NULL); + assert_int_equal(ret, EOK); + + if (exp_shortname) { + assert_string_equal(shortname, exp_shortname); + } + if (exp_domname) { + assert_string_equal(domname, exp_domname); + } + + talloc_free(shortname); + talloc_free(domname); + + assert_true(check_leaks_pop(global_talloc_context) == true); +} + +static void assert_fqname_unparseable(const char *fqname, errno_t retval) +{ + errno_t ret; + char *shortname = NULL; + char *domname = NULL; + + check_leaks_push(global_talloc_context); + + ret = sss_parse_internal_fqname(global_talloc_context, fqname, + &shortname, &domname); + assert_int_equal(ret, retval); + assert_null(shortname); + assert_null(domname); + + assert_true(check_leaks_pop(global_talloc_context) == true); +} + +static void test_sss_parse_internal_fqname(void **state) +{ + assert_parse_fqname("foo@bar", "foo", "bar"); + assert_parse_fqname("foo@bar", NULL, "bar"); + assert_parse_fqname("foo@bar", "foo", NULL); + assert_parse_fqname("foo@bar", NULL, NULL); + assert_parse_fqname("foo@bar@baz", "foo@bar", "baz"); + + assert_fqname_unparseable("foo", ERR_WRONG_NAME_FORMAT); + assert_fqname_unparseable("foo@", ERR_WRONG_NAME_FORMAT); + assert_fqname_unparseable("@", ERR_WRONG_NAME_FORMAT); + assert_fqname_unparseable("@bar", ERR_WRONG_NAME_FORMAT); + assert_fqname_unparseable(NULL, EINVAL); +} + +static void test_sss_create_internal_fqname(void **state) +{ + char *fqname = NULL; + + check_leaks_push(global_talloc_context); + + fqname = sss_create_internal_fqname(global_talloc_context, "foo", "bar"); + assert_string_equal(fqname, "foo@bar"); + talloc_zfree(fqname); + + fqname = sss_create_internal_fqname(global_talloc_context, "foo", "BAR"); + assert_string_equal(fqname, "foo@bar"); + talloc_zfree(fqname); + + fqname = sss_create_internal_fqname(global_talloc_context, "foo", NULL); + assert_null(fqname); + + fqname = sss_create_internal_fqname(global_talloc_context, NULL, "bar"); + assert_null(fqname); + + fqname = sss_create_internal_fqname(global_talloc_context, NULL, NULL); + assert_null(fqname); + + assert_true(check_leaks_pop(global_talloc_context) == true); +} + +static void test_sss_create_internal_fqname_list(void **state) +{ + char **fqlist = NULL; + const char *in_list1[] = { "aaa", "bbb", NULL }; + + check_leaks_push(global_talloc_context); + + fqlist = sss_create_internal_fqname_list(global_talloc_context, + in_list1, "DOM"); + assert_string_equal(fqlist[0], "aaa@dom"); + assert_string_equal(fqlist[1], "bbb@dom"); + assert_null(fqlist[2]); + talloc_zfree(fqlist); + + fqlist = sss_create_internal_fqname_list(global_talloc_context, + in_list1, NULL); + assert_null(fqlist); + + fqlist = sss_create_internal_fqname_list(global_talloc_context, + NULL, "DOM"); + assert_null(fqlist); + + fqlist = sss_create_internal_fqname_list(global_talloc_context, + NULL, NULL); + assert_null(fqlist); + + assert_true(check_leaks_pop(global_talloc_context) == true); +} + +static void test_sss_output_name(void **state) +{ + char *outname; + char *fqname; + + check_leaks_push(global_talloc_context); + + fqname = sss_create_internal_fqname(global_talloc_context, + "Foo Bar", "DOM"); + assert_non_null(fqname); + + outname = sss_output_name(global_talloc_context, fqname, true, 0); + assert_non_null(outname); + assert_string_equal(outname, "Foo Bar"); + talloc_zfree(outname); + + outname = sss_output_name(global_talloc_context, fqname, false, 0); + assert_non_null(outname); + assert_string_equal(outname, "foo bar"); + talloc_zfree(outname); + + outname = sss_output_name(global_talloc_context, fqname, false, '-'); + assert_non_null(outname); + assert_string_equal(outname, "foo-bar"); + talloc_zfree(outname); + + talloc_free(fqname); + assert_true(check_leaks_pop(global_talloc_context) == true); +} + +static void test_sss_get_domain_mappings_content(void **state) +{ + struct dom_list_test_ctx *test_ctx; + int ret; + struct sss_domain_info *dom; + char *content; + struct sss_domain_info *c; + + ret = sss_get_domain_mappings_content(NULL, NULL, NULL); + assert_int_equal(ret, EINVAL); + + test_ctx = talloc_get_type(*state, struct dom_list_test_ctx); + assert_non_null(test_ctx); + + dom = get_domains_head(test_ctx->dom_list); + assert_non_null(dom); + + /* no forest */ + ret = sss_get_domain_mappings_content(test_ctx, dom, &content); + assert_int_equal(ret, EOK); + assert_string_equal(content, + "[domain_realm]\n" + ".configured.dom = CONFIGURED.DOM\n" + "configured.dom = CONFIGURED.DOM\n" + ".subdom1.dom = SUBDOM1.DOM\n" + "subdom1.dom = SUBDOM1.DOM\n" + ".subdom2.dom = SUBDOM2.DOM\n" + "subdom2.dom = SUBDOM2.DOM\n" + ".subdom3.dom = SUBDOM3.DOM\n" + "subdom3.dom = SUBDOM3.DOM\n"); + talloc_free(content); + + /* IPA with forest */ + c = find_domain_by_name(dom, "subdom2.dom", true); + assert_non_null(c); + c->forest_root = find_domain_by_name(dom, "subdom1.dom", true); + assert_non_null(c->forest_root); + c->forest = discard_const_p(char, "subdom1.dom"); + + c = find_domain_by_name(dom, "subdom3.dom", true); + assert_non_null(c); + c->forest_root = find_domain_by_name(dom, "subdom1.dom", true); + assert_non_null(c->forest_root); + c->forest = discard_const_p(char, "subdom1.dom"); + + ret = sss_get_domain_mappings_content(test_ctx, dom, &content); + assert_int_equal(ret, EOK); + assert_string_equal(content, + "[domain_realm]\n" + ".configured.dom = CONFIGURED.DOM\n" + "configured.dom = CONFIGURED.DOM\n" + ".subdom1.dom = SUBDOM1.DOM\n" + "subdom1.dom = SUBDOM1.DOM\n" + ".subdom2.dom = SUBDOM2.DOM\n" + "subdom2.dom = SUBDOM2.DOM\n" + ".subdom3.dom = SUBDOM3.DOM\n" + "subdom3.dom = SUBDOM3.DOM\n" + "[capaths]\n" + "SUBDOM2.DOM = {\n" + " CONFIGURED.DOM = SUBDOM1.DOM\n" + "}\n" + "SUBDOM3.DOM = {\n" + " CONFIGURED.DOM = SUBDOM1.DOM\n" + "}\n" + "CONFIGURED.DOM = {\n" + " SUBDOM2.DOM = SUBDOM1.DOM\n" + " SUBDOM3.DOM = SUBDOM1.DOM\n" + "}\n"); + talloc_free(content); + + /* Next steps, test AD domain setup. If we join a child domain we have a + * similar case as with IPA but if we join the forest root the generate + * capaths might not be as expected. */ +} + + +static void test_sss_filter_sanitize_dn(void **state) +{ + TALLOC_CTX *tmp_ctx; + char *trimmed; + int ret; + const char *DN = "cn=user,ou=people,dc=example,dc=com"; + + tmp_ctx = talloc_new(NULL); + assert_non_null(tmp_ctx); + + /* test that we remove spaces around '=' and ','*/ + ret = sss_filter_sanitize_dn(tmp_ctx, DN, &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal(DN, trimmed); + talloc_free(trimmed); + + ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user,ou=people,dc=example,dc=com", &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal(DN, trimmed); + talloc_free(trimmed); + + ret = sss_filter_sanitize_dn(tmp_ctx, "cn= user,ou =people,dc = example,dc = com", &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal(DN, trimmed); + talloc_free(trimmed); + + ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user, ou=people ,dc=example , dc=com", &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal(DN, trimmed); + talloc_free(trimmed); + + ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user, ou=people ,dc=example , dc=com", &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal(DN, trimmed); + talloc_free(trimmed); + + ret = sss_filter_sanitize_dn(tmp_ctx, "cn= user, ou =people ,dc = example , dc = com", &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal(DN, trimmed); + talloc_free(trimmed); + + ret = sss_filter_sanitize_dn(tmp_ctx, " cn=user,ou=people,dc=example,dc=com ", &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal(DN, trimmed); + talloc_free(trimmed); + + ret = sss_filter_sanitize_dn(tmp_ctx, " cn=user, ou=people, dc=example, dc=com ", &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal(DN, trimmed); + talloc_free(trimmed); + + /* test that we keep spaces inside a value */ + ret = sss_filter_sanitize_dn(tmp_ctx, "cn = user one, ou=people branch, dc=example, dc=com", &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal("cn=user\\20one,ou=people\\20\\20branch,dc=example,dc=com", trimmed); + talloc_free(trimmed); + + /* test that we keep escape special chars like () */ + ret = sss_filter_sanitize_dn(tmp_ctx, "cn = user one, ou=p(e)ople, dc=example, dc=com", &trimmed); + assert_int_equal(ret, EOK); + assert_string_equal("cn=user\\20one,ou=p\\28e\\29ople,dc=example,dc=com", trimmed); + talloc_free(trimmed); + + talloc_free(tmp_ctx); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int rv; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_find_domain_by_sid_null, + setup_dom_list, teardown_dom_list), + cmocka_unit_test_setup_teardown(test_find_domain_by_sid, + setup_dom_list, teardown_dom_list), + cmocka_unit_test_setup_teardown(test_find_domain_by_sid_missing_sid, + setup_dom_list, teardown_dom_list), + cmocka_unit_test_setup_teardown(test_find_domain_by_sid_disabled, + setup_dom_list, teardown_dom_list), + cmocka_unit_test_setup_teardown(test_find_domain_by_name_null, + setup_dom_list, teardown_dom_list), + cmocka_unit_test_setup_teardown(test_find_domain_by_name, + setup_dom_list, teardown_dom_list), + cmocka_unit_test_setup_teardown(test_find_domain_by_name_missing_flat_name, + setup_dom_list, teardown_dom_list), + cmocka_unit_test_setup_teardown(test_find_domain_by_name_disabled, + setup_dom_list, teardown_dom_list), + cmocka_unit_test_setup_teardown(test_find_domain_by_name_ex_disabled, + setup_dom_list, teardown_dom_list), + cmocka_unit_test_setup_teardown(test_find_domain_by_object_name_ex, + setup_dom_list, teardown_dom_list), + + cmocka_unit_test_setup_teardown(test_sss_names_init, + confdb_test_setup, + confdb_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_names_ipa_ad_regexp, + confdb_test_setup, + confdb_test_teardown), + + cmocka_unit_test_setup_teardown(test_get_next_domain, + setup_dom_tree, teardown_dom_tree), + cmocka_unit_test_setup_teardown(test_get_next_domain_descend, + setup_dom_tree, teardown_dom_tree), + cmocka_unit_test_setup_teardown(test_get_next_domain_disabled, + setup_dom_tree, teardown_dom_tree), + cmocka_unit_test_setup_teardown(test_get_next_domain_flags, + setup_dom_tree, teardown_dom_tree), + + cmocka_unit_test(test_well_known_sid_to_name), + cmocka_unit_test(test_name_to_well_known_sid), + + cmocka_unit_test_setup_teardown(test_sss_filter_sanitize_for_dom, + setup_dom_list, + teardown_dom_list), + + cmocka_unit_test(test_expand_homedir_template_NULL), + cmocka_unit_test_setup_teardown(test_expand_homedir_template, + setup_homedir_ctx, + teardown_homedir_ctx), +#ifdef BUILD_SSH + cmocka_unit_test(test_textual_public_key), +#endif + cmocka_unit_test(test_replace_whitespaces), + cmocka_unit_test(test_reverse_replace_whitespaces), + cmocka_unit_test(test_guid_blob_to_string_buf), + cmocka_unit_test(test_get_last_x_chars), + cmocka_unit_test(test_concatenate_string_array), + cmocka_unit_test_setup_teardown(test_add_strings_lists, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_add_strings_lists_ex, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test(test_sss_write_krb5_conf_snippet), + cmocka_unit_test(test_get_hidden_path), + cmocka_unit_test_setup_teardown(test_sss_unique_file, + unique_file_test_setup, + unique_file_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_unique_file_destruct, + unique_file_test_setup, + unique_file_test_teardown), + cmocka_unit_test(test_sss_unique_file_neg), + cmocka_unit_test_setup_teardown(test_sss_unique_filename, + unique_file_test_setup, + unique_file_test_teardown), + cmocka_unit_test_setup_teardown(test_sss_unique_filename_destruct, + unique_file_test_setup, + unique_file_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_cert_verify_opts, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_parse_internal_fqname, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_create_internal_fqname, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_create_internal_fqname_list, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_output_name, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_get_domain_mappings_content, + setup_dom_list_with_subdomains, + teardown_dom_list), + cmocka_unit_test_setup_teardown(test_sss_ptr_hash_with_free_cb, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_ptr_hash_overwrite_with_free_cb, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_ptr_hash_with_lookup_cb, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_ptr_hash_without_cb, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_sss_filter_sanitize_dn, + setup_leak_tests, + teardown_leak_tests), + cmocka_unit_test_setup_teardown(test_mod_defaults_list, + setup_leak_tests, + teardown_leak_tests), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + /* Even though normally the tests should clean up after themselves + * they might not after a failed run. Remove the old DB to be sure */ + tests_set_cwd(); + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + test_dom_suite_setup(TESTS_PATH); + + rv = cmocka_run_group_tests(tests, NULL, NULL); + if (rv == 0) { + test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + } + return rv; +} diff --git a/src/tests/cmocka/test_utils.h b/src/tests/cmocka/test_utils.h new file mode 100644 index 0000000..7c21de8 --- /dev/null +++ b/src/tests/cmocka/test_utils.h @@ -0,0 +1,44 @@ +/* + Authors: + Lukas Slebodnik <lslebodn@redhat.com> + + Copyright (C) 2014 Red Hat + + SSSD tests: Tests for utility functions + + 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 __TESTS__CMOCKA__TEST_UTILS_H__ +#define __TESTS__CMOCKA__TEST_UTILS_H__ + +/* from src/tests/cmocka/test_sss_ssh.c */ +void test_textual_public_key(void **state); + +/* from src/tests/cmocka/test_string_utils.c */ +void test_replace_whitespaces(void **state); +void test_reverse_replace_whitespaces(void **state); +void test_guid_blob_to_string_buf(void **state); +void test_get_last_x_chars(void **state); +void test_concatenate_string_array(void **state); +void test_mod_defaults_list(void **state); + +/* from src/tests/cmocka/test_sss_ptr_hash.c */ +void test_sss_ptr_hash_with_free_cb(void **state); +void test_sss_ptr_hash_overwrite_with_free_cb(void **state); +void test_sss_ptr_hash_with_lookup_cb(void **state); +void test_sss_ptr_hash_without_cb(void **state); + + +#endif /* __TESTS__CMOCKA__TEST_UTILS_H__ */ |