diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/db/sysdb.h | 13 | ||||
-rw-r--r-- | server/db/sysdb_ops.c | 301 | ||||
-rw-r--r-- | server/tests/sysdb-tests.c | 158 |
3 files changed, 445 insertions, 27 deletions
diff --git a/server/db/sysdb.h b/server/db/sysdb.h index 4083edd8..544a81f5 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -39,6 +39,7 @@ #define SYSDB_NEXTID "nextID" #define SYSDB_UIDNUM "uidNumber" #define SYSDB_GIDNUM "gidNumber" +#define SYSDB_CREATE_TIME "createTimestamp" #define SYSDB_PW_NAME "uid" #define SYSDB_PW_PWD "userPassword" @@ -268,6 +269,18 @@ int sysdb_set_user_attr(struct sysdb_req *sysreq, struct sysdb_attrs *attributes, sysdb_callback_t fn, void *ptr); +int sysdb_add_user(struct sysdb_req *sysreq, + struct sss_domain_info *domain, + const char *name, + uid_t uid, gid_t gid, const char *gecos, + const char *homedir, const char *shell, + sysdb_callback_t fn, void *pvt); + +int sysdb_add_group(struct sysdb_req *sysreq, + struct sss_domain_info *domain, + const char *name, gid_t gid, + sysdb_callback_t fn, void *pvt); + /* legacy functions for proxy providers */ int sysdb_legacy_store_user(struct sysdb_req *sysreq, diff --git a/server/db/sysdb_ops.c b/server/db/sysdb_ops.c index 8e3eb4fa..1f53498c 100644 --- a/server/db/sysdb_ops.c +++ b/server/db/sysdb_ops.c @@ -100,13 +100,13 @@ static int sysdb_op_callback(struct ldb_request *req, struct ldb_reply *rep) } } - talloc_free(rep); - if (rep->type != LDB_REPLY_DONE) { + talloc_free(rep); return_error(cbctx, EINVAL); return LDB_ERR_OPERATIONS_ERROR; } + talloc_free(rep); return_done(cbctx); return LDB_SUCCESS; } @@ -550,6 +550,7 @@ static int sysdb_get_next_available_id(struct sysdb_req *sysreq, idctx = talloc_zero(sysreq, struct next_id_ctx); if (!idctx) return ENOMEM; + idctx->sysreq = sysreq; idctx->domain = domain; idctx->result = result; @@ -640,23 +641,22 @@ static int nextid_callback(struct ldb_request *req, struct ldb_reply *rep) switch (idctx->step) { case NEXTID_SEARCH: - if (res->count == 0) { - DEBUG(4, ("Base search returned no results\n")); - return_done(cbctx); - break; - } - - idctx->tmp_id = get_attr_as_uint32(res->msgs[0], SYSDB_NEXTID); - if (idctx->tmp_id == (uint32_t)(-1)) { - DEBUG(1, ("Invalid Next ID in domain %s\n", - idctx->domain->name)); - return_error(cbctx, ERANGE); - return LDB_ERR_OPERATIONS_ERROR; + if (res->count != 0) { + idctx->tmp_id = get_attr_as_uint32(res->msgs[0], SYSDB_NEXTID); + if (idctx->tmp_id == (uint32_t)(-1)) { + DEBUG(1, ("Invalid Next ID in domain %s\n", + idctx->domain->name)); + return_error(cbctx, ERANGE); + return LDB_ERR_OPERATIONS_ERROR; + } + } else { + DEBUG(4, ("Base search returned no results, adding min id!\n")); } if (idctx->tmp_id < idctx->domain->id_min) { DEBUG(2, ("Initializing domain next id to id min %u\n", idctx->domain->id_min)); + idctx->tmp_id = idctx->domain->id_min; } if ((idctx->domain->id_max != 0) && (idctx->tmp_id > idctx->domain->id_max)) { @@ -668,8 +668,10 @@ static int nextid_callback(struct ldb_request *req, struct ldb_reply *rep) talloc_free(res->msgs); res->msgs = NULL; + res->count = 0; idctx->step = NEXTID_VERIFY; + break; case NEXTID_VERIFY: if (res->count) { @@ -681,6 +683,7 @@ static int nextid_callback(struct ldb_request *req, struct ldb_reply *rep) idctx->tmp_id++; idctx->step = NEXTID_STORE; } + break; default: DEBUG(1, ("Invalid step, aborting.\n")); @@ -720,7 +723,7 @@ static int nextid_callback(struct ldb_request *req, struct ldb_reply *rep) } ret = ldb_build_mod_req(&nreq, ctx->ldb, idctx, msg, NULL, - idctx, sysdb_op_callback, NULL); + cbctx, sysdb_op_callback, NULL); break; default: @@ -754,6 +757,274 @@ static int nextid_callback(struct ldb_request *req, struct ldb_reply *rep) } +struct user_add_ctx { + struct sysdb_req *sysreq; + struct sysdb_cb_ctx *cbctx; + struct sss_domain_info *domain; + + const char *name; + uid_t uid; + gid_t gid; + const char *gecos; + const char *homedir; + const char *shell; + + struct next_id id; +}; + +static void user_add_id_callback(void *pvt, int error, struct ldb_result *res); +static int user_add_call(struct user_add_ctx *user_ctx); + +int sysdb_add_user(struct sysdb_req *sysreq, + struct sss_domain_info *domain, + const char *name, + uid_t uid, gid_t gid, const char *gecos, + const char *homedir, const char *shell, + sysdb_callback_t fn, void *pvt) +{ + struct user_add_ctx *user_ctx; + + if (!sysdb_req_check_running(sysreq)) { + DEBUG(2, ("Invalid request! Not running at this time.\n")); + return EINVAL; + } + + user_ctx = talloc(sysreq, struct user_add_ctx); + if (!user_ctx) return ENOMEM; + + user_ctx->cbctx = talloc_zero(user_ctx, struct sysdb_cb_ctx); + if (!user_ctx->cbctx) return ENOMEM; + + user_ctx->sysreq = sysreq; + user_ctx->domain = domain; + user_ctx->cbctx->fn = fn; + user_ctx->cbctx->pvt = pvt; + user_ctx->name = name; + user_ctx->uid = uid; + user_ctx->gid = gid; + user_ctx->gecos = gecos; + user_ctx->homedir = homedir; + user_ctx->shell = shell; + + if (uid == 0 && gid == 0) { + /* Must generate uid/gid pair */ + return sysdb_get_next_available_id(sysreq, domain, &(user_ctx->id), + user_add_id_callback, user_ctx); + } + + if (uid == 0 || gid == 0) { + /* you either set both or neither, we will not guess only one */ + DEBUG(1, ("You have to either specify both uid and gid or neither" + " (preferred) [passed in uid=%u, gid =%u]\n", uid, gid)); + return EINVAL; + } + + return user_add_call(user_ctx); +} + +static void user_add_id_callback(void *pvt, int error, struct ldb_result *res) +{ + struct user_add_ctx *user_ctx; + int ret; + + user_ctx = talloc_get_type(pvt, struct user_add_ctx); + if (error != EOK) { + return_error(user_ctx->cbctx, error); + return; + } + + /* ok id has been allocated, fill in uid and gid fields */ + user_ctx->uid = user_ctx->id.id; + user_ctx->gid = user_ctx->id.id; + + ret = user_add_call(user_ctx); + if (ret != EOK) return_error(user_ctx->cbctx, ret); +} + +static int user_add_call(struct user_add_ctx *user_ctx) +{ + struct sysdb_ctx *ctx; + struct ldb_message *msg; + struct ldb_request *req; + int flags = LDB_FLAG_MOD_ADD; + int ret; + + ctx = sysdb_req_get_ctx(user_ctx->sysreq); + + msg = ldb_msg_new(user_ctx); + if (!msg) return ENOMEM; + + msg->dn = sysdb_user_dn(ctx, msg, user_ctx->domain->name, user_ctx->name); + if (!msg->dn) return ENOMEM; + + ret = add_string(msg, flags, "objectClass", SYSDB_USER_CLASS); + if (ret != LDB_SUCCESS) return ENOMEM; + + ret = add_string(msg, flags, SYSDB_PW_NAME, user_ctx->name); + if (ret != LDB_SUCCESS) return ENOMEM; + + if (user_ctx->uid) { + ret = add_ulong(msg, flags, SYSDB_UIDNUM, + (unsigned long)(user_ctx->uid)); + if (ret != LDB_SUCCESS) return ENOMEM; + } else { + DEBUG(0, ("Cached users can't have UID == 0\n")); + return EINVAL; + } + + if (user_ctx->gid) { + ret = add_ulong(msg, flags, SYSDB_GIDNUM, + (unsigned long)(user_ctx->gid)); + if (ret != LDB_SUCCESS) return ENOMEM; + } else { + DEBUG(0, ("Cached users can't have GID == 0\n")); + return EINVAL; + } + + if (user_ctx->gecos && *user_ctx->gecos) { + ret = add_string(msg, flags, SYSDB_PW_FULLNAME, user_ctx->gecos); + if (ret != LDB_SUCCESS) return ENOMEM; + } + + if (user_ctx->homedir && *user_ctx->homedir) { + ret = add_string(msg, flags, SYSDB_PW_HOMEDIR, user_ctx->homedir); + if (ret != LDB_SUCCESS) return ENOMEM; + } + + if (user_ctx->shell && *user_ctx->shell) { + ret = add_string(msg, flags, SYSDB_PW_SHELL, user_ctx->shell); + if (ret != LDB_SUCCESS) return ENOMEM; + } + + /* creation time */ + ret = add_ulong(msg, flags, SYSDB_CREATE_TIME, (unsigned long)time(NULL)); + if (ret != LDB_SUCCESS) return ENOMEM; + + ret = ldb_build_add_req(&req, ctx->ldb, user_ctx, msg, NULL, + user_ctx->cbctx, sysdb_op_callback, NULL); + if (ret == LDB_SUCCESS) { + ret = ldb_request(ctx->ldb, req); + } + if (ret != LDB_SUCCESS) { + return sysdb_error_to_errno(ret); + } + + return EOK; +} + +struct group_add_ctx { + struct sysdb_req *sysreq; + struct sysdb_cb_ctx *cbctx; + struct sss_domain_info *domain; + + const char *name; + gid_t gid; + + struct next_id id; +}; + +static void group_add_id_callback(void *pvt, int error, struct ldb_result *res); +static int group_add_call(struct group_add_ctx *group_ctx); + +int sysdb_add_group(struct sysdb_req *sysreq, + struct sss_domain_info *domain, + const char *name, gid_t gid, + sysdb_callback_t fn, void *pvt) +{ + struct group_add_ctx *group_ctx; + + if (!sysdb_req_check_running(sysreq)) { + DEBUG(2, ("Invalid request! Not running at this time.\n")); + return EINVAL; + } + + group_ctx = talloc(sysreq, struct group_add_ctx); + if (!group_ctx) return ENOMEM; + + group_ctx->cbctx = talloc_zero(group_ctx, struct sysdb_cb_ctx); + if (!group_ctx->cbctx) return ENOMEM; + + group_ctx->sysreq = sysreq; + group_ctx->domain = domain; + group_ctx->cbctx->fn = fn; + group_ctx->cbctx->pvt = pvt; + group_ctx->name = name; + group_ctx->gid = gid; + + if (gid == 0) { + /* Must generate uid/gid pair */ + return sysdb_get_next_available_id(sysreq, domain, &(group_ctx->id), + group_add_id_callback, group_ctx); + } + + return group_add_call(group_ctx); +} + +static void group_add_id_callback(void *pvt, int error, struct ldb_result *res) +{ + struct group_add_ctx *group_ctx; + int ret; + + group_ctx = talloc_get_type(pvt, struct group_add_ctx); + if (error != EOK) { + return_error(group_ctx->cbctx, error); + return; + } + + /* ok id has been allocated, fill in uid and gid fields */ + group_ctx->gid = group_ctx->id.id; + + ret = group_add_call(group_ctx); + if (ret != EOK) return_error(group_ctx->cbctx, ret); +} + +static int group_add_call(struct group_add_ctx *group_ctx) +{ + struct sysdb_ctx *ctx; + struct ldb_message *msg; + struct ldb_request *req; + int flags = LDB_FLAG_MOD_ADD; + int ret; + + ctx = sysdb_req_get_ctx(group_ctx->sysreq); + + msg = ldb_msg_new(group_ctx); + if (!msg) return ENOMEM; + + msg->dn = sysdb_group_dn(ctx, msg, group_ctx->domain->name, group_ctx->name); + if (!msg->dn) return ENOMEM; + + ret = add_string(msg, flags, "objectClass", SYSDB_GROUP_CLASS); + if (ret != LDB_SUCCESS) return ENOMEM; + + ret = add_string(msg, flags, SYSDB_GR_NAME, group_ctx->name); + if (ret != LDB_SUCCESS) return ENOMEM; + + if (group_ctx->gid) { + ret = add_ulong(msg, flags, SYSDB_GIDNUM, + (unsigned long)(group_ctx->gid)); + if (ret != LDB_SUCCESS) return ENOMEM; + } else { + DEBUG(0, ("Cached groups can't have GID == 0\n")); + return EINVAL; + } + + /* creation time */ + ret = add_ulong(msg, flags, SYSDB_CREATE_TIME, (unsigned long)time(NULL)); + if (ret != LDB_SUCCESS) return ENOMEM; + + ret = ldb_build_add_req(&req, ctx->ldb, group_ctx, msg, NULL, + group_ctx->cbctx, sysdb_op_callback, NULL); + if (ret == LDB_SUCCESS) { + ret = ldb_request(ctx->ldb, req); + } + if (ret != LDB_SUCCESS) { + return sysdb_error_to_errno(ret); + } + + return EOK; +} + /* "sysdb_legacy_" functions diff --git a/server/tests/sysdb-tests.c b/server/tests/sysdb-tests.c index f24da579..decf6b33 100644 --- a/server/tests/sysdb-tests.c +++ b/server/tests/sysdb-tests.c @@ -32,6 +32,7 @@ struct sysdb_test_ctx { struct sysdb_ctx *sysdb; struct confdb_ctx *confdb; struct tevent_context *ev; + struct btreemap *domain_map; }; static int setup_sysdb_tests(struct sysdb_test_ctx **ctx) @@ -66,7 +67,7 @@ static int setup_sysdb_tests(struct sysdb_test_ctx **ctx) /* Connect to the conf db */ ret = confdb_init(test_ctx, test_ctx->ev, &test_ctx->confdb, conf_db); - if(ret != EOK) { + if (ret != EOK) { fail("Could not initialize connection to the confdb"); talloc_free(test_ctx); return ret; @@ -74,18 +75,27 @@ static int setup_sysdb_tests(struct sysdb_test_ctx **ctx) ret = sysdb_init(test_ctx, test_ctx->ev, test_ctx->confdb, "tests.ldb", &test_ctx->sysdb); - if(ret != EOK) { + if (ret != EOK) { fail("Could not initialize connection to the sysdb"); talloc_free(test_ctx); return ret; } + ret = confdb_get_domains(test_ctx->confdb, test_ctx, + &test_ctx->domain_map); + if (ret != EOK) { + fail("Could not initialize domains"); + talloc_free(test_ctx); + return ret; + } + *ctx = test_ctx; return EOK; } struct test_data { struct sysdb_req *sysreq; + struct sss_domain_info *domain; struct sysdb_test_ctx *ctx; const char *username; @@ -120,6 +130,27 @@ static void test_return(void *pvt, int error, struct ldb_result *ignore) data->finished = true; } +static void test_add_user(struct sysdb_req *req, void *pvt) +{ + struct test_data *data = talloc_get_type(pvt, struct test_data); + struct sysdb_ctx *ctx; + char *homedir; + char *gecos; + int ret; + + homedir = talloc_asprintf(data, "/home/testuser%d", data->uid); + gecos = talloc_asprintf(data, "Test User %d", data->uid); + + data->sysreq = req; + ctx = sysdb_req_get_ctx(req); + + ret = sysdb_add_user(req, data->domain, + data->username, data->uid, data->gid, + gecos, homedir, "/bin/bash", + data->next_fn, data); + if (ret != EOK) test_return(data, ret, NULL); +} + static void test_add_legacy_user(struct sysdb_req *req, void *pvt) { struct test_data *data = talloc_get_type(pvt, struct test_data); @@ -171,6 +202,23 @@ static void test_remove_user_by_uid(struct sysdb_req *req, void *pvt) if (ret != EOK) test_return(data, ret, NULL); } +static void test_add_group(struct sysdb_req *req, void *pvt) +{ + struct test_data *data = talloc_get_type(pvt, struct test_data); + struct sysdb_ctx *ctx; + int ret; + + data->sysreq = req; + ctx = sysdb_req_get_ctx(req); + + ret = sysdb_add_group(req, data->domain, + data->groupname, data->gid, + data->next_fn, data); + if (ret != EOK) { + test_return(data, ret, NULL); + } +} + static void test_add_legacy_group(struct sysdb_req *req, void *pvt) { struct test_data *data = talloc_get_type(pvt, struct test_data); @@ -304,6 +352,7 @@ START_TEST (test_sysdb_store_legacy_group) data->ctx = test_ctx; data->gid = _i; data->next_fn = test_return; + data->groupname = talloc_asprintf(data, "testgroup%d", _i); ret = sysdb_transaction(data, test_ctx->sysdb, test_add_legacy_group, data); @@ -738,16 +787,96 @@ START_TEST (test_sysdb_remove_local_group_by_gid) } END_TEST +START_TEST (test_sysdb_add_user) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + void *ptr; + 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; + + ptr = btreemap_get_value(test_ctx->domain_map, "LOCAL"); + data->domain = talloc_get_type(ptr, struct sss_domain_info); + if (!data->domain) { + fail("Could not set up the test (missing LOCAL domain)"); + return; + } + + data->uid = 0; + data->gid = 0; + data->next_fn = test_return; + data->username = talloc_asprintf(data, "testuser%d", _i); + + ret = sysdb_transaction(data, test_ctx->sysdb, + test_add_user, data); + if (ret == EOK) { + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not add user %s", data->username); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_add_group) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + void *ptr; + 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; + + ptr = btreemap_get_value(test_ctx->domain_map, "LOCAL"); + data->domain = talloc_get_type(ptr, struct sss_domain_info); + if (!data->domain) { + fail("Could not set up the test (missing LOCAL domain)"); + return; + } + + data->uid = 0; + data->gid = 0; + data->next_fn = test_return; + data->groupname = talloc_asprintf(data, "testgroup%d", _i); + + ret = sysdb_transaction(data, test_ctx->sysdb, + test_add_group, data); + if (ret == EOK) { + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not add group %s", data->groupname); + talloc_free(test_ctx); +} +END_TEST + Suite *create_sysdb_suite(void) { Suite *s = suite_create("sysdb"); TCase *tc_sysdb = tcase_create("SYSDB Tests"); - /* Create a new user */ + /* Create a new user (legacy) */ tcase_add_loop_test(tc_sysdb, test_sysdb_store_legacy_user,27000,27010); - /* Create a new group */ + /* Create a new group (legacy) */ tcase_add_loop_test(tc_sysdb, test_sysdb_store_legacy_group,27000,27010); /* Verify that the new group exists */ @@ -765,19 +894,24 @@ Suite *create_sysdb_suite(void) /* Remove users from their groups */ tcase_add_loop_test(tc_sysdb, test_sysdb_remove_legacy_group_member, 27000, 27010); - /* Remove half of the groups by name */ - tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group, 27000, 27005); - /* Remove the other half by gid */ - tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group_by_gid, 27005, 27010); + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group_by_gid, 27000, 27005); - /* Remove half of the users by name */ - tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_user, 27000, 27005); - /* Remove the other half by uid */ - tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_user_by_uid, 27005, 27010); + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_user_by_uid, 27000, 27005); + /* Create a new user */ + tcase_add_loop_test(tc_sysdb, test_sysdb_add_user, 27010, 27020); + + /* Create a new group */ + tcase_add_loop_test(tc_sysdb, test_sysdb_add_group, 27010, 27020); + + /* Remove half of the users by name */ + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_user, 27005, 27020); + + /* Remove half of the groups by name */ + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group, 27005, 27020); /* Add all test cases to the test suite */ suite_add_tcase(s, tc_sysdb); |