/* * Unit tests for ldap_message. * * Copyright (C) Catalyst.NET Ltd 2020 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * from cmocka.c: * These headers or their equivalents should be included prior to * including * this header file. * * #include * #include * #include * * This allows test applications to use custom definitions of C standard * library functions and types. * */ #include #include #include #include #include "lib/util/attr.h" #include "includes.h" #include "lib/util/asn1.h" #include "libcli/ldap/ldap_message.h" #include "libcli/ldap/ldap_proto.h" /* * declare the internal cmocka cm_print so we can output messages in * sub unit format */ void cm_print_error(const char * const format, ...); /* * helper function and macro to compare an ldap error code constant with the * corresponding nt_status code */ #define NT_STATUS_LDAP_V(code) (0xF2000000 | code) static void _assert_ldap_status_equal( int a, NTSTATUS b, const char * const file, const int line) { _assert_int_equal(NT_STATUS_LDAP_V(a), NT_STATUS_V(b), file, line); } #define assert_ldap_status_equal(a, b) \ _assert_ldap_status_equal((a), (b), __FILE__, __LINE__) /* * helper function and macro to assert there were no errors in the last * file operation */ static void _assert_not_ferror( FILE *f, const char * const file, const int line) { if (f == NULL || ferror(f)) { cm_print_error("ferror (%d) %s\n", errno, strerror(errno)); _fail(file, line); } } #define assert_not_ferror(f) \ _assert_not_ferror((f), __FILE__, __LINE__) struct test_ctx { }; static int setup(void **state) { struct test_ctx *test_ctx; test_ctx = talloc_zero(NULL, struct test_ctx); *state = test_ctx; return 0; } static int teardown(void **state) { struct test_ctx *test_ctx = talloc_get_type_abort(*state, struct test_ctx); TALLOC_FREE(test_ctx); return 0; } /* * Test that an empty request is handled correctly */ static void test_empty_input(void **state) { struct test_ctx *test_ctx = talloc_get_type_abort( *state, struct test_ctx); struct asn1_data *asn1; struct ldap_message *ldap_msg; NTSTATUS status; uint8_t *buf = NULL; size_t len = 0; struct ldap_request_limits limits = { .max_search_size = 256000, }; asn1 = asn1_init(test_ctx, ASN1_MAX_TREE_DEPTH); assert_non_null(asn1); asn1_load_nocopy(asn1, buf, len); ldap_msg = talloc(test_ctx, struct ldap_message); assert_non_null(ldap_msg); status = ldap_decode( asn1, &limits, samba_ldap_control_handlers(), ldap_msg); assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status); } /* * Check that a request is rejected it it's recursion depth exceeds * the maximum value specified. This test uses a very deeply nested query, * 10,000 or clauses. * */ static void test_recursion_depth_large(void **state) { struct test_ctx *test_ctx = talloc_get_type_abort( *state, struct test_ctx); struct asn1_data *asn1; struct ldap_message *ldap_msg; NTSTATUS status; FILE *f = NULL; uint8_t *buffer = NULL; const size_t BUFF_SIZE = 1048576; size_t len; struct ldap_request_limits limits = { .max_search_size = 256000, }; /* * Load a test data file containing 10,000 or clauses in encoded as * an ASN.1 packet. */ buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE); f = fopen("./libcli/ldap/tests/data/10000-or.dat", "r"); assert_not_ferror(f); len = fread(buffer, sizeof(uint8_t), BUFF_SIZE, f); assert_not_ferror(f); assert_true(len > 0); asn1 = asn1_init(test_ctx, ASN1_MAX_TREE_DEPTH); assert_non_null(asn1); asn1_load_nocopy(asn1, buffer, len); ldap_msg = talloc(test_ctx, struct ldap_message); assert_non_null(ldap_msg); status = ldap_decode( asn1, &limits, samba_ldap_control_handlers(), ldap_msg); assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status); } /* * Check that a request is not rejected it it's recursion depth equals the * maximum value */ static void test_recursion_depth_equals_max(void **state) { struct test_ctx *test_ctx = talloc_get_type_abort( *state, struct test_ctx); struct asn1_data *asn1; struct ldap_message *ldap_msg; NTSTATUS status; FILE *f = NULL; uint8_t *buffer = NULL; const size_t BUFF_SIZE = 1048576; size_t len; int ret; struct ldap_request_limits limits = { .max_search_size = 256000, }; buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE); f = fopen("./libcli/ldap/tests/data/ldap-recursive.dat", "r"); assert_not_ferror(f); len = fread(buffer, sizeof(uint8_t), BUFF_SIZE, f); assert_not_ferror(f); assert_true(len > 0); asn1 = asn1_init(test_ctx, 4); assert_non_null(asn1); asn1_load_nocopy(asn1, buffer, len); ldap_msg = talloc(test_ctx, struct ldap_message); assert_non_null(ldap_msg); status = ldap_decode( asn1, &limits, samba_ldap_control_handlers(), ldap_msg); assert_true(NT_STATUS_IS_OK(status)); ret = fclose(f); f = NULL; assert_true(ret == 0); } /* * Check that a request is rejected it it's recursion depth is greater than the * maximum value */ static void test_recursion_depth_greater_than_max(void **state) { struct test_ctx *test_ctx = talloc_get_type_abort( *state, struct test_ctx); struct asn1_data *asn1; struct ldap_message *ldap_msg; NTSTATUS status; FILE *f = NULL; uint8_t *buffer = NULL; const size_t BUFF_SIZE = 1048576; size_t len; int ret; struct ldap_request_limits limits = { .max_search_size = 256000, }; buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE); f = fopen("./libcli/ldap/tests/data/ldap-recursive.dat", "r"); assert_not_ferror(f); len = fread(buffer, sizeof(uint8_t), BUFF_SIZE, f); assert_not_ferror(f); assert_true(len > 0); asn1 = asn1_init(test_ctx, 3); assert_non_null(asn1); asn1_load_nocopy(asn1, buffer, len); ldap_msg = talloc(test_ctx, struct ldap_message); assert_non_null(ldap_msg); status = ldap_decode( asn1, &limits, samba_ldap_control_handlers(), ldap_msg); assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status); ret = fclose(f); f = NULL; assert_true(ret == 0); } /* * Check we can decode an exop response */ static void test_decode_exop_response(void **state) { struct test_ctx *test_ctx = talloc_get_type_abort( *state, struct test_ctx); struct asn1_data *asn1; struct ldap_message *ldap_msg; NTSTATUS status; FILE *f = NULL; uint8_t *buffer = NULL; const size_t BUFF_SIZE = 1048576; size_t len; int ret; struct ldap_request_limits limits = { .max_search_size = 256000, }; buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE); f = fopen("./libcli/ldap/tests/data/ldap-starttls-response.dat", "r"); assert_not_ferror(f); len = fread(buffer, sizeof(uint8_t), BUFF_SIZE, f); assert_not_ferror(f); assert_true(len > 0); asn1 = asn1_init(test_ctx, 3); assert_non_null(asn1); asn1_load_nocopy(asn1, buffer, len); ldap_msg = talloc(test_ctx, struct ldap_message); assert_non_null(ldap_msg); status = ldap_decode( asn1, &limits, samba_ldap_control_handlers(), ldap_msg); assert_true(NT_STATUS_IS_OK(status)); ret = fclose(f); f = NULL; assert_true(ret == 0); } int main(_UNUSED_ int argc, _UNUSED_ const char **argv) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown( test_empty_input, setup, teardown), cmocka_unit_test_setup_teardown( test_recursion_depth_large, setup, teardown), cmocka_unit_test_setup_teardown( test_recursion_depth_equals_max, setup, teardown), cmocka_unit_test_setup_teardown( test_recursion_depth_greater_than_max, setup, teardown), cmocka_unit_test_setup_teardown( test_decode_exop_response, setup, teardown), }; cmocka_set_message_output(CM_OUTPUT_SUBUNIT); return cmocka_run_group_tests(tests, NULL, NULL); }