summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/db/sysdb.h13
-rw-r--r--server/db/sysdb_ops.c301
-rw-r--r--server/tests/sysdb-tests.c158
3 files changed, 445 insertions, 27 deletions
diff --git a/server/db/sysdb.h b/server/db/sysdb.h
index 4083edd8d..544a81f5a 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 8e3eb4fa6..1f53498c2 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 f24da579b..decf6b339 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);