From d1ebffc7f9a074a241a75554915c6bdedf8a1f9f Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 8 Nov 2011 17:12:58 +0100 Subject: [PATCH 10/13] sudo sysdb interface: sudo rules --- src/db/sysdb.c | 19 +++ src/db/sysdb.h | 48 ++++++++ src/db/sysdb_ops.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++ src/tests/sysdb-tests.c | 178 +++++++++++++++++++++++++++++- 4 files changed, 518 insertions(+), 6 deletions(-) diff --git a/src/db/sysdb.c b/src/db/sysdb.c index ee8d2fc..c6a3fa4 100644 --- a/src/db/sysdb.c +++ b/src/db/sysdb.c @@ -233,6 +233,25 @@ struct ldb_dn *sysdb_sudocmd_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx, return dn; } +struct ldb_dn *sysdb_sudorule_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx, + const char *domain, const char *rule) +{ + errno_t ret; + char *clean_rule; + struct ldb_dn *dn; + + ret = sysdb_dn_sanitize(NULL, rule, &clean_rule); + if (ret != EOK) { + return NULL; + } + + dn = ldb_dn_new_fmt(mem_ctx, sysdb->ldb, SYSDB_TMPL_SUDORULE, + clean_rule, domain); + talloc_free(clean_rule); + + 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 b1835e6..abb5b10 100644 --- a/src/db/sysdb.h +++ b/src/db/sysdb.h @@ -38,16 +38,19 @@ #define SYSDB_NETGROUP_CONTAINER "cn=Netgroups" #define SYSDB_SUDO_CONTAINER "cn=sudo" #define SYSDB_SUDOCMDS_CONTAINER "cn=sudocmds,"SYSDB_SUDO_CONTAINER +#define SYSDB_SUDORULES_CONTAINER "cn=sudorules,"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_TMPL_SUDORULE_BASE SYSDB_SUDORULES_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_SUDORULE_CLASS "sudoRule" #define SYSDB_NAME "name" #define SYSDB_NAME_ALIAS "nameAlias" @@ -110,6 +113,7 @@ #define SYSDB_UC "objectclass="SYSDB_USER_CLASS #define SYSDB_GC "objectclass="SYSDB_GROUP_CLASS #define SYSDB_NC "objectclass="SYSDB_NETGROUP_CLASS +#define SYSDB_SUDORULEC "objectclass="SYSDB_SUDORULE_CLASS #define SYSDB_MPGC "|("SYSDB_UC")("SYSDB_GC")" #define SYSDB_PWNAM_FILTER "(&("SYSDB_UC")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))" @@ -168,6 +172,7 @@ #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_SUDORULE SYSDB_NAME"=%s,"SYSDB_TMPL_SUDORULE_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 @@ -262,6 +267,8 @@ 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); +struct ldb_dn *sysdb_sudorule_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx, + const char *domain, const char *rule); 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, @@ -473,6 +480,12 @@ int sysdb_set_sudocmd_attr(struct sysdb_ctx *sysdb, struct sysdb_attrs *attrs, int mod_op); +/* Replace sudo rule attrs */ +int sysdb_set_sudorule_attr(struct sysdb_ctx *sysdb, + const char *rule, + struct sysdb_attrs *attrs, + int mod_op); + /* Allocate a new id */ int sysdb_get_new_id(struct sysdb_ctx *sysdb, uint32_t *id); @@ -539,6 +552,16 @@ int sysdb_add_sudocmd(struct sysdb_ctx *sysdb, int cache_timeout, time_t now); +/* Add sudo rule (only basic attrs and w/o checks) */ +int sysdb_add_basic_sudorule(struct sysdb_ctx *sysdb, + const char *rule); + +int sysdb_add_sudorule(struct sysdb_ctx *sysdb, + const char *rule, + 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, @@ -617,6 +640,24 @@ errno_t sysdb_mod_netgroup_member(struct sysdb_ctx *sysdb, const char *member_netgroup, int mod_op); +enum sysdb_sudorule_mtype { + SYSDB_SUDORULE_MEMBER_USER, + SYSDB_SUDORULE_MEMBER_GROUP, + SYSDB_SUDORULE_MEMBER_COMMAND, + SYSDB_SUDORULE_MEMBER_HOST, + SYSDB_SUDORULE_MEMBER_NETGROUP, +}; + +errno_t sysdb_add_sudorule_member(struct sysdb_ctx *sysdb, + const char *sudorule, + enum sysdb_sudorule_mtype member_type, + const char *member_sudorule); + +errno_t sysdb_remove_sudorule_member(struct sysdb_ctx *sysdb, + const char *sudorule, + enum sysdb_sudorule_mtype member_type, + const char *member_sudorule); + /* Password caching function. * If you are in a transaction ignore sysdb and pass in the handle. * If you are not in a transaction pass NULL in handle and provide sysdb, @@ -706,6 +747,13 @@ int sysdb_delete_netgroup(struct sysdb_ctx *sysdb, int sysdb_delete_sudocmd(struct sysdb_ctx *sysdb, const char *command); +int sysdb_search_sudorule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *sub_filter, + const char **attrs, + size_t *msgs_count, + struct ldb_message ***msgs); + 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 3da41d6..f0e3fa9 100644 --- a/src/db/sysdb_ops.c +++ b/src/db/sysdb_ops.c @@ -651,6 +651,35 @@ done: return ret; } +/* =Replace-Attributes-On-Sudo-Rule======================================= */ + +int sysdb_set_sudorule_attr(struct sysdb_ctx *sysdb, + const char *rule, + 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_sudorule_dn(sysdb, tmp_ctx, sysdb->domain->name, rule); + 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, @@ -1592,6 +1621,119 @@ done: return ret; } +/* =Add-Basic-Sudo-Rule-NO-CHECKS============================================= */ + +/* + * member LDAP IPA + * ----------------------------------- + * user DN DN + * group DN DN + * host hostname? DN + * hostgroup netgroup DN? DN + * command DN DN + * commandgroup N/A DN + */ +int sysdb_add_basic_sudorule(struct sysdb_ctx *sysdb, + const char *rule) +{ + struct ldb_message *msg; + int ret; + + msg = ldb_msg_new(NULL); + if (!msg) { + return ENOMEM; + } + + /* sudo rule dn */ + msg->dn = sysdb_sudorule_dn(sysdb, msg, sysdb->domain->name, rule); + if (!msg->dn) { + ERROR_OUT(ret, ENOMEM, done); + } + + ret = add_string(msg, LDB_FLAG_MOD_ADD, + SYSDB_OBJECTCLASS, SYSDB_SUDORULE_CLASS); + if (ret) goto done; + + ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, rule); + 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; +} + +int sysdb_add_sudorule(struct sysdb_ctx *sysdb, + const char *rule, + 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 rule */ + ret = sysdb_add_basic_sudorule(sysdb, rule); + 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_sudorule_attr(sysdb, rule, attrs, SYSDB_MOD_REP); + +done: + if (ret == EOK) { + ret = ldb_transaction_commit(sysdb->ldb); + ret = sysdb_error_to_errno(ret); + } + + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("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, @@ -2644,6 +2786,58 @@ done: return ret; } +/* =Search-Sudo-Rules-With-Custom-Filter=================================== */ + +int sysdb_search_sudorule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *sub_filter, + const char **attrs, + size_t *msgs_count, + struct ldb_message ***msgs) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *basedn; + char *filter; + int ret; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + basedn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, + SYSDB_TMPL_SUDORULE_BASE, sysdb->domain->name); + if (!basedn) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to build base dn\n")); + ret = ENOMEM; + goto fail; + } + + filter = talloc_asprintf(tmp_ctx, "(&(%s)%s)", + SYSDB_SUDORULEC, sub_filter); + if (!filter) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to build filter\n")); + ret = ENOMEM; + goto fail; + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Search sudo rules with filter: %s\n", filter)); + + ret = sysdb_search_entry(mem_ctx, sysdb, basedn, + LDB_SCOPE_SUBTREE, filter, attrs, + msgs_count, msgs); + if (ret) { + goto fail; + } + + talloc_zfree(tmp_ctx); + return EOK; + +fail: + DEBUG(SSSDBG_OP_FAILURE, ("Error: %d (%s)\n", ret, strerror(ret))); + talloc_zfree(tmp_ctx); + return ret; +} /* ========= Authentication against cached password ============ */ @@ -3202,3 +3396,88 @@ done: talloc_free(msg); return ret; } + +errno_t sysdb_mod_sudorule_member(struct sysdb_ctx *sysdb, + const char *sudorule, + enum sysdb_sudorule_mtype member_type, + const char *member_sudorule, + int mod_op) +{ + errno_t ret; + int lret; + struct ldb_message *msg; + char *member; + const char *template; + + switch (member_type) { + case SYSDB_SUDORULE_MEMBER_USER: + template = SYSDB_TMPL_USER; + break; + case SYSDB_SUDORULE_MEMBER_GROUP: + template = SYSDB_TMPL_GROUP; + break; + case SYSDB_SUDORULE_MEMBER_COMMAND: + template = SYSDB_TMPL_SUDOCMD; + break; + case SYSDB_SUDORULE_MEMBER_HOST: + /* FIXME */ + return ENOSYS; + case SYSDB_SUDORULE_MEMBER_NETGROUP: + template = SYSDB_TMPL_NETGROUP; + break; + default: + DEBUG(SSSDBG_MINOR_FAILURE, ("Wrong sudo rule " + "member type %d\n", member_type)); + return EINVAL; + } + + msg = ldb_msg_new(NULL); + if (!msg) { + ERROR_OUT(ret, ENOMEM, done); + } + + msg->dn = sysdb_sudorule_dn(sysdb, msg, sysdb->domain->name, sudorule); + if (!msg->dn) { + ERROR_OUT(ret, ENOMEM, done); + } + + member = talloc_asprintf(msg, template, + member_sudorule, sysdb->domain->name); + if (!member) { + ret = ENOMEM; + goto done; + } + + ret = add_string(msg, mod_op, SYSDB_MEMBER, member); + if (ret != EOK) { + goto done; + } + + lret = ldb_modify(sysdb->ldb, msg); + ret = sysdb_error_to_errno(lret); + +done: + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, ("Error: %d (%s)\n", ret, strerror(ret))); + } + talloc_free(msg); + return ret; +} + +errno_t sysdb_add_sudorule_member(struct sysdb_ctx *sysdb, + const char *sudorule, + enum sysdb_sudorule_mtype member_type, + const char *member_sudorule) +{ + return sysdb_mod_sudorule_member(sysdb, sudorule, member_type, + member_sudorule, SYSDB_MOD_ADD); +} + +errno_t sysdb_remove_sudorule_member(struct sysdb_ctx *sysdb, + const char *sudorule, + enum sysdb_sudorule_mtype member_type, + const char *member_sudorule) +{ + return sysdb_mod_sudorule_member(sysdb, sudorule, member_type, + member_sudorule, SYSDB_MOD_DEL); +} diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c index 9657558..e044262 100644 --- a/src/tests/sysdb-tests.c +++ b/src/tests/sysdb-tests.c @@ -167,6 +167,7 @@ struct test_data { const char *groupname; const char *netgrname; const char *sudocmdname; + const char *sudorulename; uid_t uid; gid_t gid; const char *shell; @@ -498,6 +499,79 @@ static int test_remove_sudocmd(struct test_data *data) return sysdb_delete_sudocmd(data->ctx->sysdb, data->sudocmdname); } +static int test_add_basic_sudorule(struct test_data *data) +{ + return sysdb_add_basic_sudorule(data->ctx->sysdb, + data->sudorulename); +} + +static int test_add_sudorule(struct test_data *data) +{ + return sysdb_add_sudorule(data->ctx->sysdb, + data->sudorulename, + NULL, 30, 0); +} + +static int test_sudorule_add_members(struct test_data *data) +{ + int ret; + + ret = sysdb_add_sudorule_member(data->ctx->sysdb, + data->sudorulename, + SYSDB_SUDORULE_MEMBER_USER, + data->username); + if (ret != EOK) return ret; + + ret = sysdb_add_sudorule_member(data->ctx->sysdb, + data->sudorulename, + SYSDB_SUDORULE_MEMBER_GROUP, + data->groupname); + if (ret != EOK) return ret; + + ret = sysdb_add_sudorule_member(data->ctx->sysdb, + data->sudorulename, + SYSDB_SUDORULE_MEMBER_COMMAND, + data->sudocmdname); + if (ret != EOK) return ret; + + return EOK; +} + +static int test_sudorule_check_members(struct test_data *data) +{ + int ret; + const char *filter; + size_t count; + struct ldb_message **msgs; + struct ldb_message_element *el = NULL; + const char *attrs[] = { SYSDB_NAME, + SYSDB_MEMBER, + NULL }; + const int expected = 3; + int nmembers = 0; + int i; + + filter = talloc_asprintf(data, "(%s=%s)", SYSDB_NAME, data->sudorulename); + if (!filter) return ENOMEM; + + /* XXX - misto toho custom */ + ret = sysdb_search_sudorule(data, data->ctx->sysdb, filter, + attrs, &count, &msgs); + fail_if(ret != EOK, "Sudorules search failed\n"); + fail_if(count != 1, "More sudo rules by that name?\n"); + + el = ldb_msg_find_element(msgs[0], SYSDB_MEMBER); + if (el && el->num_values) { + nmembers = el->num_values; + } + + fail_unless(nmembers == expected, + "Expected %d members got %d\n", + expected, expected); + + return EOK; +} + START_TEST (test_sysdb_store_user) { struct sysdb_test_ctx *test_ctx; @@ -2800,6 +2874,90 @@ START_TEST (test_sysdb_delete_sudocmd) } END_TEST +START_TEST (test_sysdb_add_basic_sudorule) +{ + 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->sudorulename = talloc_asprintf(data, "testsudorule%d", _i); + + ret = test_add_basic_sudorule(data); + fail_if(ret != EOK, "Could not add sudo rule %s", data->sudorulename); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_add_sudorule) +{ + 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->sudorulename = talloc_asprintf(data, "testsudorule%d", _i); + + ret = test_add_sudorule(data); + fail_if(ret != EOK, "Could not add sudo rule %s", data->sudorulename); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_sudorule_add_members) +{ + 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->sudorulename = talloc_asprintf(data, "testsudorule%d", _i); + data->username = talloc_asprintf(data, "testuser%d", _i); + data->groupname = talloc_asprintf(data, "testgroup%d", _i+1000); + data->sudocmdname = talloc_asprintf(data, "testsudocmd%d", _i); + + ret = test_sudorule_add_members(data); + fail_if(ret != EOK, "Could not add members to sudo rule %s", + data->sudorulename); + + ret = test_sudorule_check_members(data); + fail_if(ret != EOK, "Incorrect members for sudo rule %s", + data->sudorulename); + + talloc_free(test_ctx); +} +END_TEST + START_TEST(test_odd_characters) { errno_t ret; @@ -3105,12 +3263,6 @@ Suite *create_sysdb_suite(void) /* Remove the members from the groups */ tcase_add_loop_test(tc_sysdb, test_sysdb_remove_group_member, 28010, 28020); - /* Remove the users by name */ - tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_user, 27010, 27020); - - /* Remove the groups by name */ - tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group, 28010, 28020); - /* test the ignore_not_found parameter for users */ tcase_add_test(tc_sysdb, test_sysdb_remove_nonexistent_user); @@ -3181,8 +3333,22 @@ Suite *create_sysdb_suite(void) 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_add_user,27010,27020); + tcase_add_loop_test(tc_sysdb, test_sysdb_add_group, 28010, 28020); + tcase_add_loop_test(tc_sysdb, test_sysdb_add_basic_sudorule, 27010, 27015); + tcase_add_loop_test(tc_sysdb, test_sysdb_add_sudorule, 27015, 27020); + tcase_add_loop_test(tc_sysdb, test_sysdb_sudorule_add_members, 27010, 27020); + tcase_add_loop_test(tc_sysdb, test_sysdb_delete_sudocmd, 27010, 27020); +/* ===== Cleanup ===== */ + /* Remove the users by name */ + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_user, 27010, 27020); + + /* Remove the groups by name */ + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group, 28010, 28020); + /* Add all test cases to the test suite */ suite_add_tcase(s, tc_sysdb); -- 1.7.6.4