From ce005e31b016a2f7b8a9eb85fe9132189e4a89a6 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 8 Nov 2011 16:30:36 +0100 Subject: [PATCH 09/13] sudo sysdb interface: sudo commands --- src/db/sysdb.c | 19 ++++ src/db/sysdb.h | 33 +++++++ src/db/sysdb_ops.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++- src/tests/sysdb-tests.c | 131 ++++++++++++++++++++++++++++ 4 files changed, 403 insertions(+), 1 deletions(-) diff --git a/src/db/sysdb.c b/src/db/sysdb.c index c49399f..ee8d2fc 100644 --- a/src/db/sysdb.c +++ b/src/db/sysdb.c @@ -214,6 +214,25 @@ struct ldb_dn *sysdb_netgroup_base_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_c return ldb_dn_new_fmt(mem_ctx, sysdb->ldb, SYSDB_TMPL_NETGROUP_BASE, domain); } +struct ldb_dn *sysdb_sudocmd_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx, + const char *domain, const char *command) +{ + errno_t ret; + char *clean_command; + struct ldb_dn *dn; + + ret = sysdb_dn_sanitize(NULL, command, &clean_command); + if (ret != EOK) { + return NULL; + } + + dn = ldb_dn_new_fmt(mem_ctx, sysdb->ldb, SYSDB_TMPL_SUDOCMD, + clean_command, domain); + talloc_free(clean_command); + + return dn; +} + errno_t sysdb_get_rdn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx, const char *_dn, char **_name, char **_val) { diff --git a/src/db/sysdb.h b/src/db/sysdb.h index 88767d3..b1835e6 100644 --- a/src/db/sysdb.h +++ b/src/db/sysdb.h @@ -36,14 +36,18 @@ #define SYSDB_GROUPS_CONTAINER "cn=groups" #define SYSDB_CUSTOM_CONTAINER "cn=custom" #define SYSDB_NETGROUP_CONTAINER "cn=Netgroups" +#define SYSDB_SUDO_CONTAINER "cn=sudo" +#define SYSDB_SUDOCMDS_CONTAINER "cn=sudocmds,"SYSDB_SUDO_CONTAINER #define SYSDB_TMPL_USER_BASE SYSDB_USERS_CONTAINER",cn=%s,"SYSDB_BASE #define SYSDB_TMPL_GROUP_BASE SYSDB_GROUPS_CONTAINER",cn=%s,"SYSDB_BASE #define SYSDB_TMPL_CUSTOM_BASE SYSDB_CUSTOM_CONTAINER",cn=%s,"SYSDB_BASE #define SYSDB_TMPL_NETGROUP_BASE SYSDB_NETGROUP_CONTAINER",cn=%s,"SYSDB_BASE +#define SYSDB_TMPL_SUDOCMD_BASE SYSDB_SUDOCMDS_CONTAINER",cn=%s,"SYSDB_BASE #define SYSDB_USER_CLASS "user" #define SYSDB_GROUP_CLASS "group" #define SYSDB_NETGROUP_CLASS "netgroup" +#define SYSDB_SUDOCOMMAND_CLASS "sudoCommand" #define SYSDB_NAME "name" #define SYSDB_NAME_ALIAS "nameAlias" @@ -163,6 +167,7 @@ #define SYSDB_TMPL_USER SYSDB_NAME"=%s,"SYSDB_TMPL_USER_BASE #define SYSDB_TMPL_GROUP SYSDB_NAME"=%s,"SYSDB_TMPL_GROUP_BASE #define SYSDB_TMPL_NETGROUP SYSDB_NAME"=%s,"SYSDB_TMPL_NETGROUP_BASE +#define SYSDB_TMPL_SUDOCMD SYSDB_NAME"=%s,"SYSDB_TMPL_SUDOCMD_BASE #define SYSDB_TMPL_CUSTOM_SUBTREE "cn=%s,"SYSDB_TMPL_CUSTOM_BASE #define SYSDB_TMPL_CUSTOM SYSDB_NAME"=%s,cn=%s,"SYSDB_TMPL_CUSTOM_BASE @@ -255,6 +260,8 @@ struct ldb_dn *sysdb_netgroup_dn(struct sysdb_ctx *sysdb, void *mem_ctx, const char *domain, const char *name); struct ldb_dn *sysdb_netgroup_base_dn(struct sysdb_ctx *sysdb, void *mem_ctx, const char *domain); +struct ldb_dn *sysdb_sudocmd_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx, + const char *domain, const char *command); errno_t sysdb_group_dn_name(struct sysdb_ctx *sysdb, void *mem_ctx, const char *dn_str, char **name); struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *sysdb, void *mem_ctx, @@ -429,6 +436,13 @@ int sysdb_search_netgroup_by_name(TALLOC_CTX *mem_ctx, const char **attrs, struct ldb_message **msg); +/* Search sudo command (by command) */ +int sysdb_search_sudocmd(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *command, + const char **attrs, + struct ldb_message **msg); + /* Replace entry attrs */ int sysdb_set_entry_attr(struct sysdb_ctx *sysdb, struct ldb_dn *entry_dn, @@ -453,6 +467,12 @@ int sysdb_set_netgroup_attr(struct sysdb_ctx *sysdb, struct sysdb_attrs *attrs, int mod_op); +/* Replace sudo command attrs */ +int sysdb_set_sudocmd_attr(struct sysdb_ctx *sysdb, + const char *command, + struct sysdb_attrs *attrs, + int mod_op); + /* Allocate a new id */ int sysdb_get_new_id(struct sysdb_ctx *sysdb, uint32_t *id); @@ -509,6 +529,16 @@ int sysdb_add_netgroup(struct sysdb_ctx *sysdb, int cache_timeout, time_t now); +/* Add sudo command (only basic attrs and w/o checks) */ +int sysdb_add_basic_sudocmd(struct sysdb_ctx *sysdb, + const char *command); + +int sysdb_add_sudocmd(struct sysdb_ctx *sysdb, + const char *command, + struct sysdb_attrs *attrs, + int cache_timeout, + time_t now); + /* mod_op must be either LDB_FLAG_MOD_ADD or LDB_FLAG_MOD_DELETE */ int sysdb_mod_group_member(struct sysdb_ctx *sysdb, struct ldb_dn *member_dn, @@ -673,6 +703,9 @@ int sysdb_search_netgroups(TALLOC_CTX *mem_ctx, int sysdb_delete_netgroup(struct sysdb_ctx *sysdb, const char *name); +int sysdb_delete_sudocmd(struct sysdb_ctx *sysdb, + const char *command); + errno_t sysdb_attrs_to_list(TALLOC_CTX *mem_ctx, struct sysdb_attrs **attrs, int attr_count, diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c index 3734bb1..3da41d6 100644 --- a/src/db/sysdb_ops.c +++ b/src/db/sysdb_ops.c @@ -387,7 +387,49 @@ done: } -/* =Search-Group-by-Name============================================ */ +/* =Search-Sudocmd================================================= */ + +int sysdb_search_sudocmd(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *command, + const char **attrs, + struct ldb_message **msg) +{ + TALLOC_CTX *tmp_ctx; + static const char *def_attrs[] = { SYSDB_NAME, NULL }; + struct ldb_message **msgs = NULL; + struct ldb_dn *basedn; + size_t msgs_count = 0; + int ret; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + basedn = sysdb_sudocmd_dn(sysdb, tmp_ctx, sysdb->domain->name, command); + if (!basedn) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_search_entry(tmp_ctx, sysdb, basedn, LDB_SCOPE_BASE, NULL, + attrs?attrs:def_attrs, &msgs_count, &msgs); + if (ret) { + goto done; + } + + *msg = talloc_steal(mem_ctx, msgs[0]); + +done: + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, ("Error: %d (%s)\n", ret, strerror(ret))); + } + talloc_zfree(tmp_ctx); + return ret; +} + +/* =Search-Netgroup-by-Name========================================= */ int sysdb_search_netgroup_by_name(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, @@ -580,6 +622,35 @@ done: return ret; } +/* =Replace-Attributes-On-Sudo-Command======================================= */ + +int sysdb_set_sudocmd_attr(struct sysdb_ctx *sysdb, + const char *command, + struct sysdb_attrs *attrs, + int mod_op) +{ + errno_t ret; + struct ldb_dn *dn; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + dn = sysdb_sudocmd_dn(sysdb, tmp_ctx, sysdb->domain->name, command); + if (!dn) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_set_entry_attr(sysdb, dn, attrs, mod_op); + +done: + talloc_free(tmp_ctx); + return ret; +} + /* =Get-New-ID============================================================ */ int sysdb_get_new_id(struct sysdb_ctx *sysdb, @@ -1416,6 +1487,111 @@ done: return ret; } +/* =Add-Basic-Sudo-Command-NO-CHECKS============================================= */ + +int sysdb_add_basic_sudocmd(struct sysdb_ctx *sysdb, + const char *command) +{ + struct ldb_message *msg; + int ret; + + msg = ldb_msg_new(NULL); + if (!msg) { + return ENOMEM; + } + + /* sudo command dn */ + msg->dn = sysdb_sudocmd_dn(sysdb, msg, sysdb->domain->name, command); + if (!msg->dn) { + ERROR_OUT(ret, ENOMEM, done); + } + + ret = add_string(msg, LDB_FLAG_MOD_ADD, + SYSDB_OBJECTCLASS, SYSDB_SUDOCOMMAND_CLASS); + if (ret) goto done; + + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, command); + if (ret) goto done; + + /* creation time */ + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME, + (unsigned long) time(NULL)); + if (ret) goto done; + + ret = ldb_add(sysdb->ldb, msg); + ret = sysdb_error_to_errno(ret); + +done: + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, ("Error: %d (%s)\n", ret, strerror(ret))); + } + talloc_zfree(msg); + return ret; +} + +/* FIXME - cache timeout and thus now might not be needed, we might want to just + * always rewrite sudo rules */ +int sysdb_add_sudocmd(struct sysdb_ctx *sysdb, + const char *command, + struct sysdb_attrs *attrs, + int cache_timeout, + time_t now) +{ + TALLOC_CTX *tmp_ctx; + int ret; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + ret = ldb_transaction_start(sysdb->ldb); + if (ret) { + ret = sysdb_error_to_errno(ret); + talloc_free(tmp_ctx); + return ret; + } + + /* try to add the sudo command */ + ret = sysdb_add_basic_sudocmd(sysdb, command); + if (ret && ret != EEXIST) goto done; + + if (!attrs) { + attrs = sysdb_new_attrs(tmp_ctx); + if (!attrs) { + ret = ENOMEM; + goto done; + } + } + + if (!now) { + now = time(NULL); + } + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now); + if (ret) goto done; + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE, + ((cache_timeout) ? + (now + cache_timeout) : 0)); + if (ret) goto done; + + ret = sysdb_set_sudocmd_attr(sysdb, command, attrs, SYSDB_MOD_REP); + +done: + if (ret == EOK) { + ret = ldb_transaction_commit(sysdb->ldb); + ret = sysdb_error_to_errno(ret); + } + + if (ret != EOK) { + DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret))); + ldb_transaction_cancel(sysdb->ldb); + } + talloc_zfree(tmp_ctx); + return ret; +} + /* =Store-Users-(Native/Legacy)-(replaces-existing-data)================== */ /* if one of the basic attributes is empty ("") as opposed to NULL, @@ -2426,6 +2602,49 @@ done: return ret; } +/* =Delete-Sudocmd-================================================ */ + +/* Do we need this? Or shall we delete the whole tree recursively? */ +int sysdb_delete_sudocmd(struct sysdb_ctx *sysdb, + const char *command) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_message *msg; + int ret; + + if (!command) return EINVAL; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + ret = sysdb_search_sudocmd(tmp_ctx, sysdb, + command, NULL, &msg); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_OP_FAILURE, + ("sysdb_search_sudocmd failed: %d (%s)\n", + ret, strerror(ret))); + goto done; + } else if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_FUNC, ("sudo command does not exist, nothing to delete\n")); + goto done; + } + + ret = sysdb_delete_entry(sysdb, msg->dn, false); + if (ret != EOK) { + goto done; + } + +done: + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Error: %d (%s)\n", ret, strerror(ret))); + } + talloc_free(tmp_ctx); + return ret; +} + + /* ========= Authentication against cached password ============ */ diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c index 9c080c9..9657558 100644 --- a/src/tests/sysdb-tests.c +++ b/src/tests/sysdb-tests.c @@ -166,6 +166,7 @@ struct test_data { const char *username; const char *groupname; const char *netgrname; + const char *sudocmdname; uid_t uid; gid_t gid; const char *shell; @@ -479,6 +480,24 @@ static int test_set_netgroup_attr(struct test_data *data) return ret; } +static int test_add_basic_sudocmd(struct test_data *data) +{ + return sysdb_add_basic_sudocmd(data->ctx->sysdb, + data->sudocmdname); +} + +static int test_add_sudocmd(struct test_data *data) +{ + return sysdb_add_sudocmd(data->ctx->sysdb, + data->sudocmdname, + NULL, 30, 0); +} + +static int test_remove_sudocmd(struct test_data *data) +{ + return sysdb_delete_sudocmd(data->ctx->sysdb, data->sudocmdname); +} + START_TEST (test_sysdb_store_user) { struct sysdb_test_ctx *test_ctx; @@ -2675,6 +2694,112 @@ START_TEST(test_sysdb_remove_netgroup_member) } END_TEST +START_TEST (test_sysdb_add_basic_sudocmd) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + int ret; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + if (ret != EOK) { + fail("Could not set up the test"); + return; + } + + data = talloc_zero(test_ctx, struct test_data); + data->ctx = test_ctx; + data->ev = test_ctx->ev; + data->uid = _i; /* This is kinda abuse of uid, though */ + data->sudocmdname = talloc_asprintf(data, "testsudocmd%d", _i); + + ret = test_add_basic_sudocmd(data); + + fail_if(ret != EOK, "Could not add sudo command %s", data->sudocmdname); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_add_sudocmd) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + int ret; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + if (ret != EOK) { + fail("Could not set up the test"); + return; + } + + data = talloc_zero(test_ctx, struct test_data); + data->ctx = test_ctx; + data->ev = test_ctx->ev; + data->uid = _i; /* This is kinda abuse of uid, though */ + data->sudocmdname = talloc_asprintf(data, "testsudocmd%d", _i); + + ret = test_add_sudocmd(data); + fail_if(ret != EOK, "Could not add sudo command %s", data->sudocmdname); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_search_sudocmd) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + int ret; + const char *sudocmdname; + struct ldb_message *msg; + struct ldb_dn *sudocmddn; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + if (ret != EOK) { + fail("Could not set up the test"); + return; + } + + sudocmdname = talloc_asprintf(test_ctx, "testsudocmd%d", _i); + + ret = sysdb_search_sudocmd(test_ctx, test_ctx->sysdb, + sudocmdname, NULL, &msg); + fail_if(ret != EOK, "Could not find sudo command with name %s", sudocmdname); + + sudocmddn = sysdb_sudocmd_dn(test_ctx->sysdb, test_ctx, + test_ctx->domain->name, sudocmdname); + fail_if(sudocmddn == NULL); + fail_if(ldb_dn_compare(msg->dn, sudocmddn) != 0, "Found wrong sudo command!\n"); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_delete_sudocmd) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + int ret; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + if (ret != EOK) { + fail("Could not set up the test"); + return; + } + + data = talloc_zero(test_ctx, struct test_data); + data->ctx = test_ctx; + data->ev = test_ctx->ev; + data->sudocmdname = talloc_asprintf(data, "testsudocmd%d", _i); + + ret = test_remove_sudocmd(data); + + fail_if(ret != EOK, "Could not remove sudo command with name %s", data->sudocmdname); + talloc_free(test_ctx); +} +END_TEST + START_TEST(test_odd_characters) { errno_t ret; @@ -3052,6 +3177,12 @@ Suite *create_sysdb_suite(void) /* Remove the other half by DN */ tcase_add_loop_test(tc_sysdb, test_sysdb_remove_netgroup_entry, 27005, 27010); +/* ===== SUDO TESTS ===== */ + tcase_add_loop_test(tc_sysdb, test_sysdb_add_basic_sudocmd, 27010, 27015); + tcase_add_loop_test(tc_sysdb, test_sysdb_add_sudocmd, 27015, 27020); + tcase_add_loop_test(tc_sysdb, test_sysdb_search_sudocmd, 27010, 27020); + tcase_add_loop_test(tc_sysdb, test_sysdb_delete_sudocmd, 27010, 27020); + /* Add all test cases to the test suite */ suite_add_tcase(s, tc_sysdb); -- 1.7.6.4