>From 53a9af5e3659293e2845b9a7aafea4e8bfbe818e Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Mon, 6 Jan 2014 15:27:44 +0100 Subject: [PATCH 3/6] IFP: Add utility functions Adds a number of utility functions, most importanly ifp_req_create(). The ifp_req is a structure that will be passed along with the ifp request and contains the sbus request and some ifp-specific data, like caller ID. --- Makefile.am | 29 ++++- src/responder/ifp/ifp_private.h | 16 +++ src/responder/ifp/ifpsrv_util.c | 197 ++++++++++++++++++++++++++++++++++ src/tests/cmocka/test_ifp.c | 231 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 src/responder/ifp/ifpsrv_util.c create mode 100644 src/tests/cmocka/test_ifp.c diff --git a/Makefile.am b/Makefile.am index 56d8e1df648822a1fe8b3ca5fc8995f3c78a8566..e05461a3ab400e34bf685de89f2ec038822648fe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -176,7 +176,12 @@ if HAVE_CMOCKA dp_opt_tests \ responder-get-domains-tests \ test_search_bases -endif + +if BUILD_IFP +non_interactive_cmocka_based_tests += ifp_tests +endif # BUILD_IFP + +endif # HAVE_CMOCKA check_PROGRAMS = \ stress-tests \ @@ -818,6 +823,7 @@ sssd_ifp_SOURCES = \ src/responder/ifp/ifpsrv_cmd.c \ src/responder/ifp/ifp_iface_generated.c \ src/responder/ifp/ifp_iface_generated.h \ + src/responder/ifp/ifpsrv_util.c \ $(SSSD_UTIL_OBJ) \ $(SSSD_RESPONDER_OBJ) sssd_ifp_CFLAGS = \ @@ -1676,8 +1682,29 @@ dp_opt_tests_LDADD = \ $(SSSD_INTERNAL_LTLIBS) \ libsss_test_common.la +if BUILD_IFP +ifp_tests_DEPENDENCIES = \ + $(ldblib_LTLIBRARIES) +ifp_tests_SOURCES = \ + $(TEST_MOCK_RESP_OBJ) \ + src/tests/cmocka/test_ifp.c \ + src/responder/ifp/ifpsrv_cmd.c \ + src/responder/ifp/ifpsrv_util.c +ifp_tests_CFLAGS = \ + $(AM_CFLAGS) +ifp_tests_LDFLAGS = \ + -Wl,-wrap,sbus_conn_send_reply \ + -Wl,-wrap,dbus_message_get_sender \ + -Wl,-wrap,dbus_bus_get_unix_user +ifp_tests_LDADD = \ + $(CMOCKA_LIBS) \ + $(SSSD_LIBS) \ + $(SSSD_INTERNAL_LTLIBS) \ + libsss_test_common.la endif +endif # HAVE_CMOCKA + noinst_PROGRAMS = pam_test_client if BUILD_SUDO noinst_PROGRAMS += sss_sudo_cli diff --git a/src/responder/ifp/ifp_private.h b/src/responder/ifp/ifp_private.h index b97cb8a7d9e55f550a3eda0e9acc034329d4ba17..d33009a5dbc95f8c0ca8905d9c8b9e42eaa24988 100644 --- a/src/responder/ifp/ifp_private.h +++ b/src/responder/ifp/ifp_private.h @@ -46,4 +46,20 @@ struct ifp_ctx { * It will be removed later */ int ifp_ping(struct sbus_request *dbus_req, void *data); +/* == Utility functions == */ +struct ifp_req { + struct sbus_request *dbus_req; + struct ifp_ctx *ifp_ctx; + uid_t caller; +}; + +struct ifp_req *ifp_req_create(struct sbus_request *dbus_req, + struct ifp_ctx *ifp_ctx); + +int ifp_enomem(struct ifp_req *ireq); +int ifp_return_failure(struct ifp_req *ireq, const char *err_msg); +const char *ifp_path_strip_prefix(const char *path, const char *prefix); +errno_t ifp_add_ldb_el_to_dict(DBusMessageIter *iter_dict, + struct ldb_message_element *el); + #endif /* _IFPSRV_PRIVATE_H_ */ diff --git a/src/responder/ifp/ifpsrv_util.c b/src/responder/ifp/ifpsrv_util.c new file mode 100644 index 0000000000000000000000000000000000000000..26e32f338fd9aa354ef96575852b156a52b224f0 --- /dev/null +++ b/src/responder/ifp/ifpsrv_util.c @@ -0,0 +1,197 @@ +/* + Authors: + Jakub Hrozek + Stephen Gallagher + + Copyright (C) 2013 Red Hat + + InfoPipe responder: 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 . +*/ + +#include "db/sysdb.h" +#include "responder/ifp/ifp_private.h" + +static errno_t sysbus_get_caller(struct sbus_connection *conn, + DBusMessage *message, + uid_t *_uid) +{ + const char *conn_name; + DBusError error; + unsigned long uid; + + /* Get the connection UID */ + conn_name = dbus_message_get_sender(message); + if (conn_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Critical error: D-BUS client has no unique name\n"); + return EFAULT; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "sender name is %s\n", conn_name); + + dbus_error_init(&error); + uid = dbus_bus_get_unix_user(sbus_get_connection(conn), conn_name, &error); + if (uid == (unsigned long) -1) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Could not identify unix user. Error message was '%s:%s'\n", + error.name, error.message); + dbus_error_free(&error); + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "sender ID is %lu\n", uid); + *_uid = uid; + return EOK; +} + +struct ifp_req *ifp_req_create(struct sbus_request *dbus_req, + struct ifp_ctx *ifp_ctx) +{ + struct ifp_req *req; + errno_t ret; + + if (ifp_ctx->sysbus == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Responder not connected to sysbus!\n"); + return NULL; + } + + req = talloc_zero(dbus_req, struct ifp_req); + if (req == NULL) { + return NULL; + } + + req->ifp_ctx = ifp_ctx; + req->dbus_req = dbus_req; + + ret = sysbus_get_caller(dbus_req->conn, dbus_req->message, &req->caller); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not identify caller, access denied\n"); + goto fail; + } + + return req; + +fail: + talloc_free(req); + return NULL; +} + +static inline int ifp_req_fail_and_finish(struct ifp_req *ireq, + const char *dbus_err_name, + const char *err_msg) +{ + if (ireq == NULL) return EINVAL; + + return sbus_request_fail_and_finish_str(ireq->dbus_req, + dbus_err_name, err_msg); +} + +int ifp_return_failure(struct ifp_req *ireq, const char *err_msg) +{ + return ifp_req_fail_and_finish(ireq, DBUS_ERROR_FAILED, err_msg); +} + +int ifp_enomem(struct ifp_req *ireq) +{ + return ifp_req_fail_and_finish(ireq, + DBUS_ERROR_NO_MEMORY, + "Out of memory"); +} + +const char *ifp_path_strip_prefix(const char *path, const char *prefix) +{ + if (strncmp(path, prefix, strlen(prefix)) == 0) { + return path + strlen(prefix); + } + + return NULL; +} + +errno_t ifp_add_ldb_el_to_dict(DBusMessageIter *iter_dict, + struct ldb_message_element *el) +{ + DBusMessageIter iter_dict_entry; + DBusMessageIter iter_dict_val; + DBusMessageIter iter_array; + dbus_bool_t dbret; + unsigned int i; + + if (el == NULL) { + return EINVAL; + } + + dbret = dbus_message_iter_open_container(iter_dict, + DBUS_TYPE_DICT_ENTRY, NULL, + &iter_dict_entry); + if (!dbret) { + return ENOMEM; + } + + /* Start by appending the key */ + dbret = dbus_message_iter_append_basic(&iter_dict_entry, + DBUS_TYPE_STRING, &(el->name)); + if (!dbret) { + return ENOMEM; + } + + dbret = dbus_message_iter_open_container(&iter_dict_entry, + DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_STRING_AS_STRING, + &iter_dict_val); + if (!dbret) { + return ENOMEM; + } + + /* Open container for values */ + dbret = dbus_message_iter_open_container(&iter_dict_val, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, + &iter_array); + if (!dbret) { + return ENOMEM; + } + + /* Now add all the values */ + for (i = 0; i < el->num_values; i++) { + DEBUG(SSSDBG_TRACE_FUNC, "element [%s] has value [%s]\n", + el->name, (const char *) el->values[i].data); + + dbret = dbus_message_iter_append_basic(&iter_array, + DBUS_TYPE_STRING, + &(el->values[i].data)); + if (!dbret) { + return ENOMEM; + } + } + + dbret = dbus_message_iter_close_container(&iter_dict_val, + &iter_array); + if (!dbret) { + return ENOMEM; + } + + dbret = dbus_message_iter_close_container(&iter_dict_entry, + &iter_dict_val); + if (!dbret) { + return ENOMEM; + } + + dbret = dbus_message_iter_close_container(iter_dict, + &iter_dict_entry); + if (!dbret) { + return ENOMEM; + } + + return EOK; +} diff --git a/src/tests/cmocka/test_ifp.c b/src/tests/cmocka/test_ifp.c new file mode 100644 index 0000000000000000000000000000000000000000..012dad4c5ec93fe6fbdf3e82c9ee172076550b2d --- /dev/null +++ b/src/tests/cmocka/test_ifp.c @@ -0,0 +1,231 @@ +/* + Authors: + Jakub Hrozek + + 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 . +*/ + +#include + +#include "db/sysdb.h" +#include "tests/cmocka/common_mock.h" +#include "responder/ifp/ifp_private.h" +#include "sbus/sssd_dbus_private.h" + +static struct ifp_ctx * +mock_ifp_ctx(TALLOC_CTX *mem_ctx) +{ + struct ifp_ctx *ifp_ctx; + + ifp_ctx = talloc_zero(mem_ctx, struct ifp_ctx); + assert_non_null(ifp_ctx); + + ifp_ctx->sysbus = talloc_zero(ifp_ctx, struct sysbus_ctx); + assert_non_null(ifp_ctx->sysbus); + + ifp_ctx->sysbus->conn = talloc_zero(ifp_ctx, struct sbus_connection); + assert_non_null(ifp_ctx->sysbus->conn); + + return ifp_ctx; +} + +static struct sbus_request * +mock_sbus_request(TALLOC_CTX *mem_ctx) +{ + struct sbus_request *sr; + + sr = talloc_zero(mem_ctx, struct sbus_request); + assert_non_null(sr); + + sr->conn = talloc_zero(sr, struct sbus_connection); + assert_non_null(sr->conn); + + sr->message = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL); + assert_non_null(sr->message); + dbus_message_set_serial(sr->message, 1); + + return sr; +} + +const char *__wrap_dbus_message_get_sender(DBusMessage *message) +{ + return __FILE__; +} + +unsigned long __wrap_dbus_bus_get_unix_user(DBusConnection *connection, + const char *name, + DBusError *error) +{ + return (unsigned long) geteuid(); +} + +static DBusMessage *sent_reply; + +void __wrap_sbus_conn_send_reply(struct sbus_connection *conn, DBusMessage *reply) +{ + sent_reply = reply; + return; +} + +void ifp_test_req_create(void **state) +{ + struct ifp_req *ireq; + struct sbus_request *sr; + struct ifp_ctx *ifp_ctx; + + assert_true(leak_check_setup()); + + ifp_ctx = mock_ifp_ctx(global_talloc_context); + assert_non_null(ifp_ctx); + check_leaks_push(ifp_ctx); + + sr = mock_sbus_request(ifp_ctx); + assert_non_null(sr); + check_leaks_push(sr); + + ireq = ifp_req_create(sr, ifp_ctx); + assert_non_null(ireq); + + assert_int_equal(ireq->caller, geteuid()); + talloc_free(ireq); + + assert_true(check_leaks_pop(sr) == true); + talloc_free(sr); + + assert_true(check_leaks_pop(ifp_ctx) == true); + talloc_free(ifp_ctx); + + assert_true(leak_check_teardown()); +} + +void test_path_prefix(void **state) +{ + const char *prefix = "foo"; + + assert_non_null(ifp_path_strip_prefix("foobar", prefix)); + assert_null(ifp_path_strip_prefix("notfoo", prefix)); +} + +void test_el_to_dict(void **state) +{ + static struct sbus_request *sr; + dbus_bool_t dbret; + DBusMessageIter iter; + DBusMessageIter iter_dict; + struct ldb_message_element *el; + errno_t ret; + char *attr_name; + char *attr_val; + + sr = mock_sbus_request(global_talloc_context); + assert_non_null(sr); + + el = talloc(sr, 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 *) "one"; + el->values[0].length = strlen("one") + 1; + el->values[1].data = (uint8_t *) "two"; + el->values[1].length = strlen("two") + 1; + + dbus_message_iter_init_append(sr->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(sr->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)); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const UnitTest tests[] = { + unit_test(ifp_test_req_create), + unit_test(test_path_prefix), + unit_test(test_el_to_dict), + }; + + /* 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_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 run_tests(tests); +} -- 1.9.0