mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +03:00
ldb_mdb: Apply LMDB key length restrictions at key-value layer
We need to enforce the GUID index mode so end-users do not get a supprise in mid-operation and we enforce a max key length of 511 so that the index key trunctation is done correctly. Otherwise the DB will appear to work until a very long key (DN or index) is used, after which it will be sad. Because the previous ldb_lmdb_test confirmed the key length by creating a large DN, those tests are re-worked to use the GUID index mode. In turn, new tests are written that create a special DN around the maximum key length. Finally a test is included that demonstrates that adding entries to the LMDB DB without GUID index mode fails. Signed-off-by: Gary Lockyer <gary@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org> Reviewed-by: Garming Sam <garming@catalyst.net.nz>
This commit is contained in:
parent
53d9d4974d
commit
f9a12b6433
@ -29,6 +29,8 @@
|
|||||||
#define MDB_URL_PREFIX "mdb://"
|
#define MDB_URL_PREFIX "mdb://"
|
||||||
#define MDB_URL_PREFIX_SIZE (sizeof(MDB_URL_PREFIX)-1)
|
#define MDB_URL_PREFIX_SIZE (sizeof(MDB_URL_PREFIX)-1)
|
||||||
|
|
||||||
|
#define LDB_MDB_MAX_KEY_LENGTH 511
|
||||||
|
|
||||||
#define GIGABYTE (1024*1024*1024)
|
#define GIGABYTE (1024*1024*1024)
|
||||||
|
|
||||||
int ldb_mdb_err_map(int lmdb_err)
|
int ldb_mdb_err_map(int lmdb_err)
|
||||||
@ -663,6 +665,7 @@ static int lmdb_pvt_open(TALLOC_CTX *mem_ctx,
|
|||||||
int ret;
|
int ret;
|
||||||
unsigned int mdb_flags;
|
unsigned int mdb_flags;
|
||||||
const size_t mmap_size = 8LL * GIGABYTE;
|
const size_t mmap_size = 8LL * GIGABYTE;
|
||||||
|
int lmdb_max_key_length;
|
||||||
|
|
||||||
if (flags & LDB_FLG_DONT_CREATE_DB) {
|
if (flags & LDB_FLG_DONT_CREATE_DB) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@ -720,6 +723,14 @@ static int lmdb_pvt_open(TALLOC_CTX *mem_ctx,
|
|||||||
/* Store the original pid during the LMDB open */
|
/* Store the original pid during the LMDB open */
|
||||||
lmdb->pid = getpid();
|
lmdb->pid = getpid();
|
||||||
|
|
||||||
|
lmdb_max_key_length = mdb_env_get_maxkeysize(lmdb->env);
|
||||||
|
|
||||||
|
/* This will never happen, but if it does make sure to freak out */
|
||||||
|
if (lmdb_max_key_length < LDB_MDB_MAX_KEY_LENGTH) {
|
||||||
|
talloc_free(lmdb);
|
||||||
|
return ldb_operr(ldb);
|
||||||
|
}
|
||||||
|
|
||||||
return LDB_SUCCESS;
|
return LDB_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,5 +782,13 @@ int lmdb_connect(struct ldb_context *ldb,
|
|||||||
ltdb->read_only = true;
|
ltdb->read_only = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This maximum length becomes encoded in the index values so
|
||||||
|
* must never change even if LMDB starts to allow longer keys.
|
||||||
|
* The override option is max_key_len_for_self_test, and is
|
||||||
|
* used for testing only.
|
||||||
|
*/
|
||||||
|
ltdb->max_key_length = LDB_MDB_MAX_KEY_LENGTH;
|
||||||
|
|
||||||
return init_store(ltdb, "ldb_mdb backend", ldb, options, _module);
|
return init_store(ltdb, "ldb_mdb backend", ldb, options, _module);
|
||||||
}
|
}
|
||||||
|
@ -132,12 +132,22 @@ static int ldbtest_setup(void **state)
|
|||||||
{
|
{
|
||||||
struct ldbtest_ctx *test_ctx;
|
struct ldbtest_ctx *test_ctx;
|
||||||
int ret;
|
int ret;
|
||||||
|
struct ldb_ldif *ldif;
|
||||||
|
const char *index_ldif = \
|
||||||
|
"dn: @INDEXLIST\n"
|
||||||
|
"@IDXGUID: objectUUID\n"
|
||||||
|
"@IDX_DN_GUID: GUID\n"
|
||||||
|
"\n";
|
||||||
|
|
||||||
ldbtest_noconn_setup((void **) &test_ctx);
|
ldbtest_noconn_setup((void **) &test_ctx);
|
||||||
|
|
||||||
ret = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
|
ret = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
|
||||||
assert_int_equal(ret, 0);
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
|
while ((ldif = ldb_ldif_read_string(test_ctx->ldb, &index_ldif))) {
|
||||||
|
ret = ldb_add(test_ctx->ldb, ldif->msg);
|
||||||
|
assert_int_equal(ret, LDB_SUCCESS);
|
||||||
|
}
|
||||||
*state = test_ctx;
|
*state = test_ctx;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -167,7 +177,8 @@ static void test_ldb_add_key_len_gt_max(void **state)
|
|||||||
assert_non_null(msg);
|
assert_non_null(msg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The zero terminator is part of the key
|
* The zero terminator is part of the key if we were not in
|
||||||
|
* GUID mode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
xs_size = LMDB_MAX_KEY_SIZE - 7; /* "dn=dc=" and the zero terminator */
|
xs_size = LMDB_MAX_KEY_SIZE - 7; /* "dn=dc=" and the zero terminator */
|
||||||
@ -181,8 +192,11 @@ static void test_ldb_add_key_len_gt_max(void **state)
|
|||||||
ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
|
ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
|
||||||
assert_int_equal(ret, 0);
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
|
ret = ldb_msg_add_string(msg, "objectUUID", "0123456789abcdef");
|
||||||
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
ret = ldb_add(test_ctx->ldb, msg);
|
ret = ldb_add(test_ctx->ldb, msg);
|
||||||
assert_int_equal(ret, LDB_ERR_PROTOCOL_ERROR);
|
assert_int_equal(ret, LDB_SUCCESS);
|
||||||
|
|
||||||
talloc_free(tmp_ctx);
|
talloc_free(tmp_ctx);
|
||||||
}
|
}
|
||||||
@ -204,7 +218,8 @@ static void test_ldb_add_key_len_eq_max(void **state)
|
|||||||
assert_non_null(msg);
|
assert_non_null(msg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The zero terminator is part of the key
|
* The zero terminator is part of the key if we were not in
|
||||||
|
* GUID mode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
xs_size = LMDB_MAX_KEY_SIZE - 7; /* "dn=dc=" and the zero terminator */
|
xs_size = LMDB_MAX_KEY_SIZE - 7; /* "dn=dc=" and the zero terminator */
|
||||||
@ -217,12 +232,145 @@ static void test_ldb_add_key_len_eq_max(void **state)
|
|||||||
ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
|
ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
|
||||||
assert_int_equal(ret, 0);
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
|
ret = ldb_msg_add_string(msg, "objectUUID", "0123456789abcdef");
|
||||||
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
ret = ldb_add(test_ctx->ldb, msg);
|
ret = ldb_add(test_ctx->ldb, msg);
|
||||||
assert_int_equal(ret, 0);
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
talloc_free(tmp_ctx);
|
talloc_free(tmp_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ldbtest_setup_noguid(void **state)
|
||||||
|
{
|
||||||
|
struct ldbtest_ctx *test_ctx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ldbtest_noconn_setup((void **) &test_ctx);
|
||||||
|
|
||||||
|
ret = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
|
||||||
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
|
*state = test_ctx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ldb_add_special_key_len_gt_max(void **state)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int xs_size = 0;
|
||||||
|
struct ldb_message *msg;
|
||||||
|
struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
|
||||||
|
struct ldbtest_ctx);
|
||||||
|
char *xs = NULL;
|
||||||
|
TALLOC_CTX *tmp_ctx;
|
||||||
|
|
||||||
|
tmp_ctx = talloc_new(test_ctx);
|
||||||
|
assert_non_null(tmp_ctx);
|
||||||
|
|
||||||
|
msg = ldb_msg_new(tmp_ctx);
|
||||||
|
assert_non_null(msg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The zero terminator is part of the key if we were not in
|
||||||
|
* GUID mode
|
||||||
|
*/
|
||||||
|
|
||||||
|
xs_size = LMDB_MAX_KEY_SIZE - 5; /* "dn=@" and the zero terminator */
|
||||||
|
xs_size += 1; /* want key on char too long */
|
||||||
|
xs = talloc_zero_size(tmp_ctx, (xs_size + 1));
|
||||||
|
memset(xs, 'x', xs_size);
|
||||||
|
|
||||||
|
msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "@%s", xs);
|
||||||
|
assert_non_null(msg->dn);
|
||||||
|
|
||||||
|
ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
|
||||||
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
|
ret = ldb_add(test_ctx->ldb, msg);
|
||||||
|
assert_int_equal(ret, LDB_ERR_PROTOCOL_ERROR);
|
||||||
|
|
||||||
|
talloc_free(tmp_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ldb_add_special_key_len_eq_max(void **state)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int xs_size = 0;
|
||||||
|
struct ldb_message *msg;
|
||||||
|
struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
|
||||||
|
struct ldbtest_ctx);
|
||||||
|
char *xs = NULL;
|
||||||
|
TALLOC_CTX *tmp_ctx;
|
||||||
|
|
||||||
|
tmp_ctx = talloc_new(test_ctx);
|
||||||
|
assert_non_null(tmp_ctx);
|
||||||
|
|
||||||
|
msg = ldb_msg_new(tmp_ctx);
|
||||||
|
assert_non_null(msg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The zero terminator is part of the key if we were not in
|
||||||
|
* GUID mode
|
||||||
|
*/
|
||||||
|
|
||||||
|
xs_size = LMDB_MAX_KEY_SIZE - 5; /* "dn=@" and the zero terminator */
|
||||||
|
xs = talloc_zero_size(tmp_ctx, (xs_size + 1));
|
||||||
|
memset(xs, 'x', xs_size);
|
||||||
|
|
||||||
|
msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "@%s", xs);
|
||||||
|
assert_non_null(msg->dn);
|
||||||
|
|
||||||
|
ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
|
||||||
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
|
ret = ldb_add(test_ctx->ldb, msg);
|
||||||
|
assert_int_equal(ret, LDB_SUCCESS);
|
||||||
|
|
||||||
|
talloc_free(tmp_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ldb_add_dn_no_guid_mode(void **state)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int xs_size = 0;
|
||||||
|
struct ldb_message *msg;
|
||||||
|
struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
|
||||||
|
struct ldbtest_ctx);
|
||||||
|
char *xs = NULL;
|
||||||
|
TALLOC_CTX *tmp_ctx;
|
||||||
|
|
||||||
|
tmp_ctx = talloc_new(test_ctx);
|
||||||
|
assert_non_null(tmp_ctx);
|
||||||
|
|
||||||
|
msg = ldb_msg_new(tmp_ctx);
|
||||||
|
assert_non_null(msg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The zero terminator is part of the key if we were not in
|
||||||
|
* GUID mode
|
||||||
|
*/
|
||||||
|
|
||||||
|
xs_size = LMDB_MAX_KEY_SIZE - 7; /* "dn=dc=" and the zero terminator */
|
||||||
|
xs_size += 1; /* want key on char too long */
|
||||||
|
xs = talloc_zero_size(tmp_ctx, (xs_size + 1));
|
||||||
|
memset(xs, 'x', xs_size);
|
||||||
|
|
||||||
|
msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "dc=%s", xs);
|
||||||
|
assert_non_null(msg->dn);
|
||||||
|
|
||||||
|
ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
|
||||||
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
|
ret = ldb_msg_add_string(msg, "objectUUID", "0123456789abcdef");
|
||||||
|
assert_int_equal(ret, 0);
|
||||||
|
|
||||||
|
ret = ldb_add(test_ctx->ldb, msg);
|
||||||
|
assert_int_equal(ret, LDB_ERR_UNWILLING_TO_PERFORM);
|
||||||
|
|
||||||
|
talloc_free(tmp_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv)
|
int main(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
const struct CMUnitTest tests[] = {
|
const struct CMUnitTest tests[] = {
|
||||||
@ -234,6 +382,18 @@ int main(int argc, const char **argv)
|
|||||||
test_ldb_add_key_len_gt_max,
|
test_ldb_add_key_len_gt_max,
|
||||||
ldbtest_setup,
|
ldbtest_setup,
|
||||||
ldbtest_teardown),
|
ldbtest_teardown),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_ldb_add_special_key_len_eq_max,
|
||||||
|
ldbtest_setup_noguid,
|
||||||
|
ldbtest_teardown),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_ldb_add_special_key_len_gt_max,
|
||||||
|
ldbtest_setup_noguid,
|
||||||
|
ldbtest_teardown),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_ldb_add_dn_no_guid_mode,
|
||||||
|
ldbtest_setup_noguid,
|
||||||
|
ldbtest_teardown),
|
||||||
};
|
};
|
||||||
|
|
||||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||||
|
Loading…
Reference in New Issue
Block a user