TODO COMMIT MESSAGE ldb_mdb: Apply LMDB key length restrictions at key-value layer
authorGary Lockyer <gary@catalyst.net.nz>
Tue, 6 Mar 2018 02:27:51 +0000 (15:27 +1300)
committerStefan Metzmacher <metze@samba.org>
Thu, 12 Apr 2018 14:27:17 +0000 (16:27 +0200)
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.

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
lib/ldb/ldb_mdb/ldb_mdb.c
lib/ldb/tests/ldb_lmdb_test.c

index 5169e51e76a1410ee36866e09188892885801b45..422ed71219a7b89129cc527525963b6c513c0bab 100644 (file)
@@ -29,6 +29,8 @@
 #define MDB_URL_PREFIX         "mdb://"
 #define MDB_URL_PREFIX_SIZE    (sizeof(MDB_URL_PREFIX)-1)
 
+#define LDB_MDB_MAX_KEY_LENGTH 511
+
 #define MEGABYTE (1024*1024)
 #define GIGABYTE (1024*1024*1024)
 
@@ -659,6 +661,7 @@ static int lmdb_pvt_open(TALLOC_CTX *mem_ctx,
 {
        int ret;
        unsigned int mdb_flags;
+       int lmdb_max_key_length;
 
        if (flags & LDB_FLG_DONT_CREATE_DB) {
                struct stat st;
@@ -713,6 +716,14 @@ static int lmdb_pvt_open(TALLOC_CTX *mem_ctx,
        /* Store the original pid during the LMDB open */
        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;
 
 }
@@ -764,6 +775,15 @@ int lmdb_connect(struct ldb_context *ldb,
        if (flags & LDB_FLG_RDONLY) {
                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);
 }
 
index e47c7dbc70f3907b3da65720d47d6e4f93e8a098..eece2be805708fb5dc8d6edbfb5a4695e74e11c4 100644 (file)
@@ -132,12 +132,22 @@ static int ldbtest_setup(void **state)
 {
        struct ldbtest_ctx *test_ctx;
        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);
 
        ret = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
        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;
        return 0;
 }
@@ -167,7 +177,8 @@ static void test_ldb_add_key_len_gt_max(void **state)
        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 */
@@ -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");
        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_PROTOCOL_ERROR);
+       assert_int_equal(ret, LDB_SUCCESS);
 
        talloc_free(tmp_ctx);
 }
@@ -204,7 +218,8 @@ static void test_ldb_add_key_len_eq_max(void **state)
        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 */
@@ -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");
        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, 0);
 
        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)
 {
        const struct CMUnitTest tests[] = {
@@ -234,6 +382,18 @@ int main(int argc, const char **argv)
                        test_ldb_add_key_len_gt_max,
                        ldbtest_setup,
                        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);