From 51e5796950c7e429838d7283441af63171339657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Thu, 10 Dec 2015 14:08:52 +0100 Subject: [PATCH 20/49] IPA: add ipa_get_rdn and ipa_check_rdn To exploit knowledge of IPA LDAP hierarchy. Reviewed-by: Sumit Bose (cherry picked from commit b407fe0474a674bb42f0f42ab47c7f530a07a367) --- Makefile.am | 22 ++++ src/providers/ipa/ipa_dn.c | 145 ++++++++++++++++++++++++++ src/providers/ipa/ipa_dn.h | 43 ++++++++ src/tests/cmocka/test_ipa_dn.c | 228 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 438 insertions(+) create mode 100644 src/providers/ipa/ipa_dn.c create mode 100644 src/providers/ipa/ipa_dn.h create mode 100644 src/tests/cmocka/test_ipa_dn.c diff --git a/Makefile.am b/Makefile.am index 8b57640cacd0e1f30f3d1270a92521c55ba0e026..6efb5ea7f81642292b39a44e7e2029a2757e47ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -245,6 +245,7 @@ if HAVE_CMOCKA test_cert_utils \ test_ldap_id_cleanup \ test_data_provider_be \ + test_ipa_dn \ $(NULL) if HAVE_LIBRESOLV @@ -642,6 +643,7 @@ dist_noinst_HEADERS = \ src/providers/ipa/ipa_hostid.h \ src/providers/ipa/ipa_opts.h \ src/providers/ipa/ipa_srv.h \ + src/providers/ipa/ipa_dn.h \ src/providers/ad/ad_srv.h \ src/providers/proxy/proxy.h \ src/tools/tools_util.h \ @@ -2631,6 +2633,25 @@ test_data_provider_be_LDADD = \ libdlopen_test_providers.la \ $(NULL) +test_ipa_dn_SOURCES = \ + src/providers/ipa/ipa_dn.c \ + src/tests/cmocka/test_ipa_dn.c \ + $(NULL) +test_ipa_dn_CFLAGS = \ + $(AM_CFLAGS) \ + -DUNIT_TESTING \ + $(NULL) +test_ipa_dn_LDFLAGS = \ + -Wl,-wrap,_tevent_add_timer \ + $(NULL) +test_ipa_dn_LDADD = \ + $(CMOCKA_LIBS) \ + $(SSSD_LIBS) \ + $(SSSD_INTERNAL_LTLIBS) \ + $(LIBADD_DL) \ + libsss_test_common.la \ + $(NULL) + endif # HAVE_CMOCKA noinst_PROGRAMS = pam_test_client @@ -2983,6 +3004,7 @@ libsss_ipa_la_SOURCES = \ src/providers/ipa/ipa_selinux_maps.c \ src/providers/ipa/ipa_srv.c \ src/providers/ipa/ipa_idmap.c \ + src/providers/ipa/ipa_dn.c \ src/providers/ad/ad_opts.c \ src/providers/ad/ad_common.c \ src/providers/ad/ad_common.h \ diff --git a/src/providers/ipa/ipa_dn.c b/src/providers/ipa/ipa_dn.c new file mode 100644 index 0000000000000000000000000000000000000000..c58e014f8c83d39f2c558449702a02dc6fdb0713 --- /dev/null +++ b/src/providers/ipa/ipa_dn.c @@ -0,0 +1,145 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#include +#include +#include "db/sysdb.h" +#include "providers/ipa/ipa_dn.h" + +static bool check_dn(struct ldb_dn *dn, + const char *rdn_attr, + va_list in_ap) +{ + const struct ldb_val *ldbval; + const char *strval; + const char *ldbattr; + const char *attr; + const char *val; + va_list ap; + int num_comp; + int comp; + + /* check RDN attribute */ + ldbattr = ldb_dn_get_rdn_name(dn); + if (ldbattr == NULL || strcasecmp(ldbattr, rdn_attr) != 0) { + return false; + } + + /* Check DN components. First we check if all attr=value pairs match input. + * Then we check that the next attribute is a domain component. + */ + + comp = 1; + num_comp = ldb_dn_get_comp_num(dn); + + va_copy(ap, in_ap); + while ((attr = va_arg(ap, const char *)) != NULL) { + val = va_arg(ap, const char *); + if (val == NULL) { + goto vafail; + } + + if (comp > num_comp) { + goto vafail; + } + + ldbattr = ldb_dn_get_component_name(dn, comp); + if (ldbattr == NULL || strcasecmp(ldbattr, attr) != 0) { + goto vafail; + } + + ldbval = ldb_dn_get_component_val(dn, comp); + if (ldbval == NULL) { + goto vafail; + } + + strval = (const char *)ldbval->data; + if (strval == NULL || strncasecmp(strval, val, ldbval->length) != 0) { + goto vafail; + } + + comp++; + } + va_end(ap); + + ldbattr = ldb_dn_get_component_name(dn, comp); + if (ldbattr == NULL || strcmp(ldbattr, "dc") != 0) { + return false; + } + + return true; + +vafail: + va_end(ap); + return false; +} + +errno_t _ipa_get_rdn(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *obj_dn, + char **_rdn_val, + const char *rdn_attr, + ...) +{ + const struct ldb_val *val; + struct ldb_dn *dn; + errno_t ret; + bool bret; + va_list ap; + char *rdn; + + dn = ldb_dn_new(mem_ctx, sysdb_ctx_get_ldb(sysdb), obj_dn); + if (dn == NULL) { + return ENOMEM; + } + + va_start(ap, rdn_attr); + bret = check_dn(dn, rdn_attr, ap); + va_end(ap); + if (bret == false) { + ret = ENOENT; + goto done; + } + + if (_rdn_val == NULL) { + ret = EOK; + goto done; + } + + val = ldb_dn_get_rdn_val(dn); + if (val == NULL || val->data == NULL) { + ret = EINVAL; + goto done; + } + + rdn = talloc_strndup(mem_ctx, (const char*)val->data, val->length); + if (rdn == NULL) { + ret = ENOMEM; + goto done; + } + + *_rdn_val = rdn; + + ret = EOK; + +done: + talloc_free(dn); + return ret; +} diff --git a/src/providers/ipa/ipa_dn.h b/src/providers/ipa/ipa_dn.h new file mode 100644 index 0000000000000000000000000000000000000000..f889c3ee6548c6d4cf719441bbe2f0c7caa1a579 --- /dev/null +++ b/src/providers/ipa/ipa_dn.h @@ -0,0 +1,43 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#ifndef IPA_DN_H_ +#define IPA_DN_H_ + +#include +#include "db/sysdb.h" + +errno_t _ipa_get_rdn(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *obj_dn, + char **_rdn_val, + const char *rdn_attr, + ...); + +#define ipa_get_rdn(mem_ctx, sysdb, dn, _rdn_val, rdn_attr, ...) \ + _ipa_get_rdn(mem_ctx, sysdb, dn, _rdn_val, rdn_attr, ##__VA_ARGS__, NULL) + +#define ipa_check_rdn(sysdb, dn, rdn_attr, ...) \ + _ipa_get_rdn(NULL, sysdb, dn, NULL, rdn_attr, ##__VA_ARGS__, NULL) + +#define ipa_check_rdn_bool(sysdb, dn, rdn_attr, ...) \ + ((bool)(ipa_check_rdn(sysdb, dn, rdn_attr, ##__VA_ARGS__) == EOK)) + +#endif /* IPA_DN_H_ */ diff --git a/src/tests/cmocka/test_ipa_dn.c b/src/tests/cmocka/test_ipa_dn.c new file mode 100644 index 0000000000000000000000000000000000000000..a6e26ec31ff25519ad895ef934dac0e3a3dd83ae --- /dev/null +++ b/src/tests/cmocka/test_ipa_dn.c @@ -0,0 +1,228 @@ +/* + Authors: + Pavel Březina + + 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 . +*/ + +#include +#include +#include + +#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); +} + +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 deside 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; +} -- 2.5.0