diff options
author | Karolin Seeger <kseeger@samba.org> | 2020-04-28 13:50:10 +0200 |
---|---|---|
committer | Karolin Seeger <kseeger@samba.org> | 2020-04-28 13:50:10 +0200 |
commit | 94c0fbebc6cf32b82e3b25a1f254e22efbc3fcdf (patch) | |
tree | ca120a420e7e9cb2a8fda38d17c914d72a9c8b1b | |
parent | 34bfaafc77e35c8808452f678111c73bbbdd705d (diff) | |
parent | 86c951a1336b2073f35c9ccc16673b6f99251fcc (diff) | |
download | samba-94c0fbebc6cf32b82e3b25a1f254e22efbc3fcdf.tar.gz |
Merge tag 'samba-4.11.8' into v4-11-test
samba: tag release samba-4.11.8
34 files changed, 1362 insertions, 68 deletions
diff --git a/WHATSNEW.txt b/WHATSNEW.txt index 89e730b605e..3502516273e 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -1,4 +1,65 @@ ============================== + Release Notes for Samba 4.11.8 + April 28, 2020 + ============================== + + +This is a security release in order to address the following defects: + +o CVE-2020-10700: Use-after-free in Samba AD DC LDAP Server with ASQ +o CVE-2020-10704: LDAP Denial of Service (stack overflow) in Samba AD DC + + +======= +Details +======= + +o CVE-2020-10700: + A client combining the 'ASQ' and 'Paged Results' LDAP controls can cause a + use-after-free in Samba's AD DC LDAP server. +o CVE-2020-10704: + A deeply nested filter in an un-authenticated LDAP search can exhaust the + LDAP server's stack memory causing a SIGSEGV. + +For more details, please refer to the security advisories. + + +Changes since 4.11.7 +-------------------- + +o Andrew Bartlett <abartlet@samba.org> + * BUG 14331: CVE-2020-10700: Fix use-after-free in AD DC LDAP server when + ASQ and paged_results combined. + +o Gary Lockyer <gary@catalyst.net.nz> + * BUG 20454: CVE-2020-10704: Fix LDAP Denial of Service (stack overflow) in + Samba AD DC. + + +####################################### +Reporting bugs & Development Discussion +####################################### + +Please discuss this release on the samba-technical mailing list or by +joining the #samba-technical IRC channel on irc.freenode.net. + +If you do report problems then please try to send high quality +feedback. If you don't provide vital information to help us track down +the problem then you will probably be ignored. All bug reports should +be filed under the "Samba 4.1 and newer" product in the project's Bugzilla +database (https://bugzilla.samba.org/). + + +====================================================================== +== Our Code, Our Bugs, Our Responsibility. +== The Samba Team +====================================================================== + + +Release notes for older releases follow: +---------------------------------------- + + ============================== Release Notes for Samba 4.11.7 March 10, 2020 ============================== @@ -59,8 +120,8 @@ database (https://bugzilla.samba.org/). ====================================================================== -Release notes for older releases follow: ----------------------------------------- +---------------------------------------------------------------------- + ============================== Release Notes for Samba 4.11.6 diff --git a/auth/gensec/gensec_util.c b/auth/gensec/gensec_util.c index 20c9c2a1fbb..e185acc0c20 100644 --- a/auth/gensec/gensec_util.c +++ b/auth/gensec/gensec_util.c @@ -76,7 +76,7 @@ NTSTATUS gensec_generate_session_info_pac(TALLOC_CTX *mem_ctx, static bool gensec_gssapi_check_oid(const DATA_BLOB *blob, const char *oid) { bool ret = false; - struct asn1_data *data = asn1_init(NULL); + struct asn1_data *data = asn1_init(NULL, ASN1_MAX_TREE_DEPTH); if (!data) return false; diff --git a/docs-xml/smbdotconf/ldap/ldapmaxanonrequest.xml b/docs-xml/smbdotconf/ldap/ldapmaxanonrequest.xml new file mode 100644 index 00000000000..61bdcec674d --- /dev/null +++ b/docs-xml/smbdotconf/ldap/ldapmaxanonrequest.xml @@ -0,0 +1,18 @@ +<samba:parameter name="ldap max anonymous request size" + context="G" + type="integer" + xmlns:samba="http://www.samba.org/samba/DTD/samba-doc"> +<description> + <para> + This parameter specifies the maximum permitted size (in bytes) + for an LDAP request received on an anonymous connection. + </para> + + <para> + If the request size exceeds this limit the request will be + rejected. + </para> +</description> +<value type="default">256000</value> +<value type="example">500000</value> +</samba:parameter> diff --git a/docs-xml/smbdotconf/ldap/ldapmaxauthrequest.xml b/docs-xml/smbdotconf/ldap/ldapmaxauthrequest.xml new file mode 100644 index 00000000000..c5934f73f95 --- /dev/null +++ b/docs-xml/smbdotconf/ldap/ldapmaxauthrequest.xml @@ -0,0 +1,18 @@ +<samba:parameter name="ldap max authenticated request size" + context="G" + type="integer" + xmlns:samba="http://www.samba.org/samba/DTD/samba-doc"> +<description> + <para> + This parameter specifies the maximum permitted size (in bytes) + for an LDAP request received on an authenticated connection. + </para> + + <para> + If the request size exceeds this limit the request will be + rejected. + </para> +</description> +<value type="default">16777216</value> +<value type="example">4194304</value> +</samba:parameter> diff --git a/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml b/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml new file mode 100644 index 00000000000..ebeb0816c01 --- /dev/null +++ b/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml @@ -0,0 +1,18 @@ +<samba:parameter name="ldap max search request size" + context="G" + type="integer" + xmlns:samba="http://www.samba.org/samba/DTD/samba-doc"> +<description> + <para> + This parameter specifies the maximum permitted size (in bytes) + for an LDAP search request. + </para> + + <para> + If the request size exceeds this limit the request will be + rejected. + </para> +</description> +<value type="default">256000</value> +<value type="example">4194304</value> +</samba:parameter> diff --git a/lib/ldb/ABI/ldb-2.0.10.sigs b/lib/ldb/ABI/ldb-2.0.10.sigs new file mode 100644 index 00000000000..5049dc64ce1 --- /dev/null +++ b/lib/ldb/ABI/ldb-2.0.10.sigs @@ -0,0 +1,283 @@ +ldb_add: int (struct ldb_context *, const struct ldb_message *) +ldb_any_comparison: int (struct ldb_context *, void *, ldb_attr_handler_t, const struct ldb_val *, const struct ldb_val *) +ldb_asprintf_errstring: void (struct ldb_context *, const char *, ...) +ldb_attr_casefold: char *(TALLOC_CTX *, const char *) +ldb_attr_dn: int (const char *) +ldb_attr_in_list: int (const char * const *, const char *) +ldb_attr_list_copy: const char **(TALLOC_CTX *, const char * const *) +ldb_attr_list_copy_add: const char **(TALLOC_CTX *, const char * const *, const char *) +ldb_base64_decode: int (char *) +ldb_base64_encode: char *(TALLOC_CTX *, const char *, int) +ldb_binary_decode: struct ldb_val (TALLOC_CTX *, const char *) +ldb_binary_encode: char *(TALLOC_CTX *, struct ldb_val) +ldb_binary_encode_string: char *(TALLOC_CTX *, const char *) +ldb_build_add_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_del_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_extended_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const char *, void *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_mod_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_rename_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_search_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, const char *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_build_search_req_ex: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, struct ldb_parse_tree *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *) +ldb_casefold: char *(struct ldb_context *, TALLOC_CTX *, const char *, size_t) +ldb_casefold_default: char *(void *, TALLOC_CTX *, const char *, size_t) +ldb_check_critical_controls: int (struct ldb_control **) +ldb_comparison_binary: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *) +ldb_comparison_fold: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *) +ldb_connect: int (struct ldb_context *, const char *, unsigned int, const char **) +ldb_control_to_string: char *(TALLOC_CTX *, const struct ldb_control *) +ldb_controls_except_specified: struct ldb_control **(struct ldb_control **, TALLOC_CTX *, struct ldb_control *) +ldb_debug: void (struct ldb_context *, enum ldb_debug_level, const char *, ...) +ldb_debug_add: void (struct ldb_context *, const char *, ...) +ldb_debug_end: void (struct ldb_context *, enum ldb_debug_level) +ldb_debug_set: void (struct ldb_context *, enum ldb_debug_level, const char *, ...) +ldb_delete: int (struct ldb_context *, struct ldb_dn *) +ldb_dn_add_base: bool (struct ldb_dn *, struct ldb_dn *) +ldb_dn_add_base_fmt: bool (struct ldb_dn *, const char *, ...) +ldb_dn_add_child: bool (struct ldb_dn *, struct ldb_dn *) +ldb_dn_add_child_fmt: bool (struct ldb_dn *, const char *, ...) +ldb_dn_add_child_val: bool (struct ldb_dn *, const char *, struct ldb_val) +ldb_dn_alloc_casefold: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_alloc_linearized: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_canonical_ex_string: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_canonical_string: char *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_check_local: bool (struct ldb_module *, struct ldb_dn *) +ldb_dn_check_special: bool (struct ldb_dn *, const char *) +ldb_dn_compare: int (struct ldb_dn *, struct ldb_dn *) +ldb_dn_compare_base: int (struct ldb_dn *, struct ldb_dn *) +ldb_dn_copy: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_escape_value: char *(TALLOC_CTX *, struct ldb_val) +ldb_dn_extended_add_syntax: int (struct ldb_context *, unsigned int, const struct ldb_dn_extended_syntax *) +ldb_dn_extended_filter: void (struct ldb_dn *, const char * const *) +ldb_dn_extended_syntax_by_name: const struct ldb_dn_extended_syntax *(struct ldb_context *, const char *) +ldb_dn_from_ldb_val: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const struct ldb_val *) +ldb_dn_get_casefold: const char *(struct ldb_dn *) +ldb_dn_get_comp_num: int (struct ldb_dn *) +ldb_dn_get_component_name: const char *(struct ldb_dn *, unsigned int) +ldb_dn_get_component_val: const struct ldb_val *(struct ldb_dn *, unsigned int) +ldb_dn_get_extended_comp_num: int (struct ldb_dn *) +ldb_dn_get_extended_component: const struct ldb_val *(struct ldb_dn *, const char *) +ldb_dn_get_extended_linearized: char *(TALLOC_CTX *, struct ldb_dn *, int) +ldb_dn_get_ldb_context: struct ldb_context *(struct ldb_dn *) +ldb_dn_get_linearized: const char *(struct ldb_dn *) +ldb_dn_get_parent: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *) +ldb_dn_get_rdn_name: const char *(struct ldb_dn *) +ldb_dn_get_rdn_val: const struct ldb_val *(struct ldb_dn *) +ldb_dn_has_extended: bool (struct ldb_dn *) +ldb_dn_is_null: bool (struct ldb_dn *) +ldb_dn_is_special: bool (struct ldb_dn *) +ldb_dn_is_valid: bool (struct ldb_dn *) +ldb_dn_map_local: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) +ldb_dn_map_rebase_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) +ldb_dn_map_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *) +ldb_dn_minimise: bool (struct ldb_dn *) +ldb_dn_new: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *) +ldb_dn_new_fmt: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *, ...) +ldb_dn_remove_base_components: bool (struct ldb_dn *, unsigned int) +ldb_dn_remove_child_components: bool (struct ldb_dn *, unsigned int) +ldb_dn_remove_extended_components: void (struct ldb_dn *) +ldb_dn_replace_components: bool (struct ldb_dn *, struct ldb_dn *) +ldb_dn_set_component: int (struct ldb_dn *, int, const char *, const struct ldb_val) +ldb_dn_set_extended_component: int (struct ldb_dn *, const char *, const struct ldb_val *) +ldb_dn_update_components: int (struct ldb_dn *, const struct ldb_dn *) +ldb_dn_validate: bool (struct ldb_dn *) +ldb_dump_results: void (struct ldb_context *, struct ldb_result *, FILE *) +ldb_error_at: int (struct ldb_context *, int, const char *, const char *, int) +ldb_errstring: const char *(struct ldb_context *) +ldb_extended: int (struct ldb_context *, const char *, void *, struct ldb_result **) +ldb_extended_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_filter_attrs: int (struct ldb_context *, const struct ldb_message *, const char * const *, struct ldb_message *) +ldb_filter_from_tree: char *(TALLOC_CTX *, const struct ldb_parse_tree *) +ldb_get_config_basedn: struct ldb_dn *(struct ldb_context *) +ldb_get_create_perms: unsigned int (struct ldb_context *) +ldb_get_default_basedn: struct ldb_dn *(struct ldb_context *) +ldb_get_event_context: struct tevent_context *(struct ldb_context *) +ldb_get_flags: unsigned int (struct ldb_context *) +ldb_get_opaque: void *(struct ldb_context *, const char *) +ldb_get_root_basedn: struct ldb_dn *(struct ldb_context *) +ldb_get_schema_basedn: struct ldb_dn *(struct ldb_context *) +ldb_global_init: int (void) +ldb_handle_get_event_context: struct tevent_context *(struct ldb_handle *) +ldb_handle_new: struct ldb_handle *(TALLOC_CTX *, struct ldb_context *) +ldb_handle_use_global_event_context: void (struct ldb_handle *) +ldb_handler_copy: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *) +ldb_handler_fold: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *) +ldb_init: struct ldb_context *(TALLOC_CTX *, struct tevent_context *) +ldb_ldif_message_redacted_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *) +ldb_ldif_message_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *) +ldb_ldif_parse_modrdn: int (struct ldb_context *, const struct ldb_ldif *, TALLOC_CTX *, struct ldb_dn **, struct ldb_dn **, bool *, struct ldb_dn **, struct ldb_dn **) +ldb_ldif_read: struct ldb_ldif *(struct ldb_context *, int (*)(void *), void *) +ldb_ldif_read_file: struct ldb_ldif *(struct ldb_context *, FILE *) +ldb_ldif_read_file_state: struct ldb_ldif *(struct ldb_context *, struct ldif_read_file_state *) +ldb_ldif_read_free: void (struct ldb_context *, struct ldb_ldif *) +ldb_ldif_read_string: struct ldb_ldif *(struct ldb_context *, const char **) +ldb_ldif_write: int (struct ldb_context *, int (*)(void *, const char *, ...), void *, const struct ldb_ldif *) +ldb_ldif_write_file: int (struct ldb_context *, FILE *, const struct ldb_ldif *) +ldb_ldif_write_redacted_trace_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *) +ldb_ldif_write_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *) +ldb_load_modules: int (struct ldb_context *, const char **) +ldb_map_add: int (struct ldb_module *, struct ldb_request *) +ldb_map_delete: int (struct ldb_module *, struct ldb_request *) +ldb_map_init: int (struct ldb_module *, const struct ldb_map_attribute *, const struct ldb_map_objectclass *, const char * const *, const char *, const char *) +ldb_map_modify: int (struct ldb_module *, struct ldb_request *) +ldb_map_rename: int (struct ldb_module *, struct ldb_request *) +ldb_map_search: int (struct ldb_module *, struct ldb_request *) +ldb_match_message: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, enum ldb_scope, bool *) +ldb_match_msg: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope) +ldb_match_msg_error: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope, bool *) +ldb_match_msg_objectclass: int (const struct ldb_message *, const char *) +ldb_mod_register_control: int (struct ldb_module *, const char *) +ldb_modify: int (struct ldb_context *, const struct ldb_message *) +ldb_modify_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_module_call_chain: char *(struct ldb_request *, TALLOC_CTX *) +ldb_module_connect_backend: int (struct ldb_context *, const char *, const char **, struct ldb_module **) +ldb_module_done: int (struct ldb_request *, struct ldb_control **, struct ldb_extended *, int) +ldb_module_flags: uint32_t (struct ldb_context *) +ldb_module_get_ctx: struct ldb_context *(struct ldb_module *) +ldb_module_get_name: const char *(struct ldb_module *) +ldb_module_get_ops: const struct ldb_module_ops *(struct ldb_module *) +ldb_module_get_private: void *(struct ldb_module *) +ldb_module_init_chain: int (struct ldb_context *, struct ldb_module *) +ldb_module_load_list: int (struct ldb_context *, const char **, struct ldb_module *, struct ldb_module **) +ldb_module_new: struct ldb_module *(TALLOC_CTX *, struct ldb_context *, const char *, const struct ldb_module_ops *) +ldb_module_next: struct ldb_module *(struct ldb_module *) +ldb_module_popt_options: struct poptOption **(struct ldb_context *) +ldb_module_send_entry: int (struct ldb_request *, struct ldb_message *, struct ldb_control **) +ldb_module_send_referral: int (struct ldb_request *, char *) +ldb_module_set_next: void (struct ldb_module *, struct ldb_module *) +ldb_module_set_private: void (struct ldb_module *, void *) +ldb_modules_hook: int (struct ldb_context *, enum ldb_module_hook_type) +ldb_modules_list_from_string: const char **(struct ldb_context *, TALLOC_CTX *, const char *) +ldb_modules_load: int (const char *, const char *) +ldb_msg_add: int (struct ldb_message *, const struct ldb_message_element *, int) +ldb_msg_add_empty: int (struct ldb_message *, const char *, int, struct ldb_message_element **) +ldb_msg_add_fmt: int (struct ldb_message *, const char *, const char *, ...) +ldb_msg_add_linearized_dn: int (struct ldb_message *, const char *, struct ldb_dn *) +ldb_msg_add_steal_string: int (struct ldb_message *, const char *, char *) +ldb_msg_add_steal_value: int (struct ldb_message *, const char *, struct ldb_val *) +ldb_msg_add_string: int (struct ldb_message *, const char *, const char *) +ldb_msg_add_value: int (struct ldb_message *, const char *, const struct ldb_val *, struct ldb_message_element **) +ldb_msg_canonicalize: struct ldb_message *(struct ldb_context *, const struct ldb_message *) +ldb_msg_check_string_attribute: int (const struct ldb_message *, const char *, const char *) +ldb_msg_copy: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *) +ldb_msg_copy_attr: int (struct ldb_message *, const char *, const char *) +ldb_msg_copy_shallow: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *) +ldb_msg_diff: struct ldb_message *(struct ldb_context *, struct ldb_message *, struct ldb_message *) +ldb_msg_difference: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message *, struct ldb_message *, struct ldb_message **) +ldb_msg_element_compare: int (struct ldb_message_element *, struct ldb_message_element *) +ldb_msg_element_compare_name: int (struct ldb_message_element *, struct ldb_message_element *) +ldb_msg_element_equal_ordered: bool (const struct ldb_message_element *, const struct ldb_message_element *) +ldb_msg_find_attr_as_bool: int (const struct ldb_message *, const char *, int) +ldb_msg_find_attr_as_dn: struct ldb_dn *(struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, const char *) +ldb_msg_find_attr_as_double: double (const struct ldb_message *, const char *, double) +ldb_msg_find_attr_as_int: int (const struct ldb_message *, const char *, int) +ldb_msg_find_attr_as_int64: int64_t (const struct ldb_message *, const char *, int64_t) +ldb_msg_find_attr_as_string: const char *(const struct ldb_message *, const char *, const char *) +ldb_msg_find_attr_as_uint: unsigned int (const struct ldb_message *, const char *, unsigned int) +ldb_msg_find_attr_as_uint64: uint64_t (const struct ldb_message *, const char *, uint64_t) +ldb_msg_find_common_values: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message_element *, struct ldb_message_element *, uint32_t) +ldb_msg_find_duplicate_val: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message_element *, struct ldb_val **, uint32_t) +ldb_msg_find_element: struct ldb_message_element *(const struct ldb_message *, const char *) +ldb_msg_find_ldb_val: const struct ldb_val *(const struct ldb_message *, const char *) +ldb_msg_find_val: struct ldb_val *(const struct ldb_message_element *, struct ldb_val *) +ldb_msg_new: struct ldb_message *(TALLOC_CTX *) +ldb_msg_normalize: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_message **) +ldb_msg_remove_attr: void (struct ldb_message *, const char *) +ldb_msg_remove_element: void (struct ldb_message *, struct ldb_message_element *) +ldb_msg_rename_attr: int (struct ldb_message *, const char *, const char *) +ldb_msg_sanity_check: int (struct ldb_context *, const struct ldb_message *) +ldb_msg_sort_elements: void (struct ldb_message *) +ldb_next_del_trans: int (struct ldb_module *) +ldb_next_end_trans: int (struct ldb_module *) +ldb_next_init: int (struct ldb_module *) +ldb_next_prepare_commit: int (struct ldb_module *) +ldb_next_read_lock: int (struct ldb_module *) +ldb_next_read_unlock: int (struct ldb_module *) +ldb_next_remote_request: int (struct ldb_module *, struct ldb_request *) +ldb_next_request: int (struct ldb_module *, struct ldb_request *) +ldb_next_start_trans: int (struct ldb_module *) +ldb_op_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_options_copy: const char **(TALLOC_CTX *, const char **) +ldb_options_find: const char *(struct ldb_context *, const char **, const char *) +ldb_options_get: const char **(struct ldb_context *) +ldb_pack_data: int (struct ldb_context *, const struct ldb_message *, struct ldb_val *, uint32_t) +ldb_parse_control_from_string: struct ldb_control *(struct ldb_context *, TALLOC_CTX *, const char *) +ldb_parse_control_strings: struct ldb_control **(struct ldb_context *, TALLOC_CTX *, const char **) +ldb_parse_tree: struct ldb_parse_tree *(TALLOC_CTX *, const char *) +ldb_parse_tree_attr_replace: void (struct ldb_parse_tree *, const char *, const char *) +ldb_parse_tree_copy_shallow: struct ldb_parse_tree *(TALLOC_CTX *, const struct ldb_parse_tree *) +ldb_parse_tree_walk: int (struct ldb_parse_tree *, int (*)(struct ldb_parse_tree *, void *), void *) +ldb_qsort: void (void * const, size_t, size_t, void *, ldb_qsort_cmp_fn_t) +ldb_register_backend: int (const char *, ldb_connect_fn, bool) +ldb_register_extended_match_rule: int (struct ldb_context *, const struct ldb_extended_match_rule *) +ldb_register_hook: int (ldb_hook_fn) +ldb_register_module: int (const struct ldb_module_ops *) +ldb_rename: int (struct ldb_context *, struct ldb_dn *, struct ldb_dn *) +ldb_reply_add_control: int (struct ldb_reply *, const char *, bool, void *) +ldb_reply_get_control: struct ldb_control *(struct ldb_reply *, const char *) +ldb_req_get_custom_flags: uint32_t (struct ldb_request *) +ldb_req_is_untrusted: bool (struct ldb_request *) +ldb_req_location: const char *(struct ldb_request *) +ldb_req_mark_trusted: void (struct ldb_request *) +ldb_req_mark_untrusted: void (struct ldb_request *) +ldb_req_set_custom_flags: void (struct ldb_request *, uint32_t) +ldb_req_set_location: void (struct ldb_request *, const char *) +ldb_request: int (struct ldb_context *, struct ldb_request *) +ldb_request_add_control: int (struct ldb_request *, const char *, bool, void *) +ldb_request_done: int (struct ldb_request *, int) +ldb_request_get_control: struct ldb_control *(struct ldb_request *, const char *) +ldb_request_get_status: int (struct ldb_request *) +ldb_request_replace_control: int (struct ldb_request *, const char *, bool, void *) +ldb_request_set_state: void (struct ldb_request *, int) +ldb_reset_err_string: void (struct ldb_context *) +ldb_save_controls: int (struct ldb_control *, struct ldb_request *, struct ldb_control ***) +ldb_schema_attribute_add: int (struct ldb_context *, const char *, unsigned int, const char *) +ldb_schema_attribute_add_with_syntax: int (struct ldb_context *, const char *, unsigned int, const struct ldb_schema_syntax *) +ldb_schema_attribute_by_name: const struct ldb_schema_attribute *(struct ldb_context *, const char *) +ldb_schema_attribute_fill_with_syntax: int (struct ldb_context *, TALLOC_CTX *, const char *, unsigned int, const struct ldb_schema_syntax *, struct ldb_schema_attribute *) +ldb_schema_attribute_remove: void (struct ldb_context *, const char *) +ldb_schema_attribute_remove_flagged: void (struct ldb_context *, unsigned int) +ldb_schema_attribute_set_override_handler: void (struct ldb_context *, ldb_attribute_handler_override_fn_t, void *) +ldb_schema_set_override_GUID_index: void (struct ldb_context *, const char *, const char *) +ldb_schema_set_override_indexlist: void (struct ldb_context *, bool) +ldb_search: int (struct ldb_context *, TALLOC_CTX *, struct ldb_result **, struct ldb_dn *, enum ldb_scope, const char * const *, const char *, ...) +ldb_search_default_callback: int (struct ldb_request *, struct ldb_reply *) +ldb_sequence_number: int (struct ldb_context *, enum ldb_sequence_type, uint64_t *) +ldb_set_create_perms: void (struct ldb_context *, unsigned int) +ldb_set_debug: int (struct ldb_context *, void (*)(void *, enum ldb_debug_level, const char *, va_list), void *) +ldb_set_debug_stderr: int (struct ldb_context *) +ldb_set_default_dns: void (struct ldb_context *) +ldb_set_errstring: void (struct ldb_context *, const char *) +ldb_set_event_context: void (struct ldb_context *, struct tevent_context *) +ldb_set_flags: void (struct ldb_context *, unsigned int) +ldb_set_modules_dir: void (struct ldb_context *, const char *) +ldb_set_opaque: int (struct ldb_context *, const char *, void *) +ldb_set_require_private_event_context: void (struct ldb_context *) +ldb_set_timeout: int (struct ldb_context *, struct ldb_request *, int) +ldb_set_timeout_from_prev_req: int (struct ldb_context *, struct ldb_request *, struct ldb_request *) +ldb_set_utf8_default: void (struct ldb_context *) +ldb_set_utf8_fns: void (struct ldb_context *, void *, char *(*)(void *, void *, const char *, size_t)) +ldb_setup_wellknown_attributes: int (struct ldb_context *) +ldb_should_b64_encode: int (struct ldb_context *, const struct ldb_val *) +ldb_standard_syntax_by_name: const struct ldb_schema_syntax *(struct ldb_context *, const char *) +ldb_strerror: const char *(int) +ldb_string_to_time: time_t (const char *) +ldb_string_utc_to_time: time_t (const char *) +ldb_timestring: char *(TALLOC_CTX *, time_t) +ldb_timestring_utc: char *(TALLOC_CTX *, time_t) +ldb_transaction_cancel: int (struct ldb_context *) +ldb_transaction_cancel_noerr: int (struct ldb_context *) +ldb_transaction_commit: int (struct ldb_context *) +ldb_transaction_prepare_commit: int (struct ldb_context *) +ldb_transaction_start: int (struct ldb_context *) +ldb_unpack_data: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *) +ldb_unpack_data_flags: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *, unsigned int) +ldb_unpack_get_format: int (const struct ldb_val *, uint32_t *) +ldb_val_dup: struct ldb_val (TALLOC_CTX *, const struct ldb_val *) +ldb_val_equal_exact: int (const struct ldb_val *, const struct ldb_val *) +ldb_val_map_local: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *) +ldb_val_map_remote: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *) +ldb_val_string_cmp: int (const struct ldb_val *, const char *) +ldb_val_to_time: int (const struct ldb_val *, time_t *) +ldb_valid_attr_name: int (const char *) +ldb_vdebug: void (struct ldb_context *, enum ldb_debug_level, const char *, va_list) +ldb_wait: int (struct ldb_handle *, enum ldb_wait_type) diff --git a/lib/ldb/ABI/pyldb-util-2.0.10.sigs b/lib/ldb/ABI/pyldb-util-2.0.10.sigs new file mode 100644 index 00000000000..74d6719d2bc --- /dev/null +++ b/lib/ldb/ABI/pyldb-util-2.0.10.sigs @@ -0,0 +1,2 @@ +pyldb_Dn_FromDn: PyObject *(struct ldb_dn *) +pyldb_Object_AsDn: bool (TALLOC_CTX *, PyObject *, struct ldb_context *, struct ldb_dn **) diff --git a/lib/ldb/modules/asq.c b/lib/ldb/modules/asq.c index 7482de826f0..4eba941ae0b 100644 --- a/lib/ldb/modules/asq.c +++ b/lib/ldb/modules/asq.c @@ -311,12 +311,9 @@ static int asq_build_multiple_requests(struct asq_context *ac, bool *terminated) static int asq_search_continue(struct asq_context *ac) { - struct ldb_context *ldb; bool terminated = false; int ret; - ldb = ldb_module_get_ctx(ac->module); - switch (ac->step) { case ASQ_SEARCH_BASE: @@ -328,7 +325,7 @@ static int asq_search_continue(struct asq_context *ac) ac->step = ASQ_SEARCH_MULTI; - return ldb_request(ldb, ac->reqs[ac->cur_req]); + return ldb_next_request(ac->module, ac->reqs[ac->cur_req]); case ASQ_SEARCH_MULTI: @@ -339,7 +336,7 @@ static int asq_search_continue(struct asq_context *ac) return asq_search_terminate(ac); } - return ldb_request(ldb, ac->reqs[ac->cur_req]); + return ldb_next_request(ac->module, ac->reqs[ac->cur_req]); } return LDB_ERR_OPERATIONS_ERROR; @@ -347,14 +344,11 @@ static int asq_search_continue(struct asq_context *ac) static int asq_search(struct ldb_module *module, struct ldb_request *req) { - struct ldb_context *ldb; struct ldb_request *base_req; struct ldb_control *control; struct asq_context *ac; int ret; - ldb = ldb_module_get_ctx(module); - /* check if there's an ASQ control */ control = ldb_request_get_control(req, LDB_CONTROL_ASQ_OID); if (control == NULL) { @@ -385,7 +379,7 @@ static int asq_search(struct ldb_module *module, struct ldb_request *req) ac->step = ASQ_SEARCH_BASE; - return ldb_request(ldb, base_req); + return ldb_next_request(ac->module, base_req); } static int asq_init(struct ldb_module *module) diff --git a/lib/ldb/wscript b/lib/ldb/wscript index c3f2f42b8c1..43571a7593f 100644 --- a/lib/ldb/wscript +++ b/lib/ldb/wscript @@ -1,7 +1,7 @@ #!/usr/bin/env python APPNAME = 'ldb' -VERSION = '2.0.9' +VERSION = '2.0.10' import sys, os diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c index 883d4167bf4..e0c6adec9c8 100644 --- a/lib/param/loadparm.c +++ b/lib/param/loadparm.c @@ -3025,6 +3025,13 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx) lpcfg_do_global_parameter(lp_ctx, "debug encryption", "no"); + lpcfg_do_global_parameter( + lp_ctx, "ldap max anonymous request size", "256000"); + lpcfg_do_global_parameter( + lp_ctx, "ldap max authenticated request size", "16777216"); + lpcfg_do_global_parameter( + lp_ctx, "ldap max search request size", "256000"); + for (i = 0; parm_table[i].label; i++) { if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) { lp_ctx->flags[i] |= FLAG_DEFAULT; diff --git a/lib/util/asn1.c b/lib/util/asn1.c index 51da5424956..32d7981d28f 100644 --- a/lib/util/asn1.c +++ b/lib/util/asn1.c @@ -36,15 +36,19 @@ struct asn1_data { off_t ofs; struct nesting *nesting; bool has_error; + unsigned depth; + unsigned max_depth; }; /* allocate an asn1 structure */ -struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx) +struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx, unsigned max_depth) { struct asn1_data *ret = talloc_zero(mem_ctx, struct asn1_data); if (ret == NULL) { DEBUG(0,("asn1_init failed! out of memory\n")); + return ret; } + ret->max_depth = max_depth; return ret; } @@ -480,6 +484,11 @@ bool asn1_check_BOOLEAN(struct asn1_data *data, bool v) /* load a struct asn1_data structure with a lump of data, ready to be parsed */ bool asn1_load(struct asn1_data *data, DATA_BLOB blob) { + /* + * Save the maximum depth + */ + unsigned max_depth = data->max_depth; + ZERO_STRUCTP(data); data->data = (uint8_t *)talloc_memdup(data, blob.data, blob.length); if (!data->data) { @@ -487,6 +496,7 @@ bool asn1_load(struct asn1_data *data, DATA_BLOB blob) return false; } data->length = blob.length; + data->max_depth = max_depth; return true; } @@ -637,6 +647,16 @@ bool asn1_start_tag(struct asn1_data *data, uint8_t tag) uint8_t b; struct nesting *nesting; + /* + * Check the depth of the parse tree and prevent it from growing + * too large. + */ + data->depth++; + if (data->depth > data->max_depth) { + data->has_error = true; + return false; + } + if (!asn1_read_uint8(data, &b)) return false; @@ -693,6 +713,9 @@ bool asn1_end_tag(struct asn1_data *data) { struct nesting *nesting; + if (data->depth > 0) { + data->depth--; + } /* make sure we read it all */ if (asn1_tag_remaining(data) != 0) { data->has_error = true; @@ -1103,9 +1126,14 @@ bool asn1_extract_blob(struct asn1_data *asn1, TALLOC_CTX *mem_ctx, */ void asn1_load_nocopy(struct asn1_data *data, uint8_t *buf, size_t len) { + /* + * Save max_depth + */ + unsigned max_depth = data->max_depth; ZERO_STRUCTP(data); data->data = buf; data->length = len; + data->max_depth = max_depth; } int asn1_peek_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size) @@ -1131,3 +1159,10 @@ int asn1_peek_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size) *packet_size = size; return 0; } + +/* + * Get the length of the ASN.1 data + */ +size_t asn1_get_length(const struct asn1_data *asn1) { + return asn1->length; +} diff --git a/lib/util/asn1.h b/lib/util/asn1.h index ddd69863574..de92a767f14 100644 --- a/lib/util/asn1.h +++ b/lib/util/asn1.h @@ -45,7 +45,14 @@ typedef struct asn1_data ASN1_DATA; #define ASN1_MAX_OIDS 20 -struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx); +/* + * The maximum permitted depth for an ASN.1 parse tree, the limit is chosen + * to align with the value for windows. Note that this value will trigger + * ASAN stack overflow errors. + */ +#define ASN1_MAX_TREE_DEPTH 512 + +struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx, unsigned max_depth); void asn1_free(struct asn1_data *data); bool asn1_has_error(const struct asn1_data *data); void asn1_set_error(struct asn1_data *data); @@ -99,5 +106,6 @@ bool asn1_extract_blob(struct asn1_data *asn1, TALLOC_CTX *mem_ctx, DATA_BLOB *pblob); void asn1_load_nocopy(struct asn1_data *data, uint8_t *buf, size_t len); int asn1_peek_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size); +size_t asn1_get_length(const struct asn1_data *asn1); #endif /* _ASN_1_H */ diff --git a/lib/util/tests/asn1_tests.c b/lib/util/tests/asn1_tests.c index e4b386ad785..ab5262c4ffb 100644 --- a/lib/util/tests/asn1_tests.c +++ b/lib/util/tests/asn1_tests.c @@ -330,7 +330,7 @@ static bool test_asn1_Integer(struct torture_context *tctx) DATA_BLOB blob; int val; - data = asn1_init(mem_ctx); + data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) { goto err; } diff --git a/libcli/auth/spnego_parse.c b/libcli/auth/spnego_parse.c index f538b44552c..f7f19b10778 100644 --- a/libcli/auth/spnego_parse.c +++ b/libcli/auth/spnego_parse.c @@ -296,7 +296,7 @@ ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data return ret; } - asn1 = asn1_init(mem_ctx); + asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { return -1; } @@ -339,7 +339,7 @@ ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego) { - struct asn1_data *asn1 = asn1_init(mem_ctx); + struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); ssize_t ret = -1; if (asn1 == NULL) { @@ -411,7 +411,7 @@ bool spnego_write_mech_types(TALLOC_CTX *mem_ctx, DATA_BLOB *blob) { bool ret = false; - struct asn1_data *asn1 = asn1_init(mem_ctx); + struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { return false; diff --git a/libcli/cldap/cldap.c b/libcli/cldap/cldap.c index daba37a21d7..25c1b40f8d9 100644 --- a/libcli/cldap/cldap.c +++ b/libcli/cldap/cldap.c @@ -111,6 +111,11 @@ struct cldap_search_state { struct tevent_req *req; }; +/* + * For CLDAP we limit the maximum search request size to 4kb + */ +#define MAX_SEARCH_REQUEST 4096 + static int cldap_socket_destructor(struct cldap_socket *c) { while (c->searches.list) { @@ -224,12 +229,15 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c, void *p; struct cldap_search_state *search; NTSTATUS status; + struct ldap_request_limits limits = { + .max_search_size = MAX_SEARCH_REQUEST + }; if (in->recv_errno != 0) { goto error; } - asn1 = asn1_init(in); + asn1 = asn1_init(in, ASN1_MAX_TREE_DEPTH); if (!asn1) { goto nomem; } @@ -242,7 +250,7 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c, } /* this initial decode is used to find the message id */ - status = ldap_decode(asn1, NULL, in->ldap_msg); + status = ldap_decode(asn1, &limits, NULL, in->ldap_msg); if (!NT_STATUS_IS_OK(status)) { goto nterror; } @@ -770,6 +778,9 @@ NTSTATUS cldap_search_recv(struct tevent_req *req, struct cldap_search_state); struct ldap_message *ldap_msg; NTSTATUS status; + struct ldap_request_limits limits = { + .max_search_size = MAX_SEARCH_REQUEST + }; if (tevent_req_is_nterror(req, &status)) { goto failed; @@ -780,7 +791,7 @@ NTSTATUS cldap_search_recv(struct tevent_req *req, goto nomem; } - status = ldap_decode(state->response.asn1, NULL, ldap_msg); + status = ldap_decode(state->response.asn1, &limits, NULL, ldap_msg); if (!NT_STATUS_IS_OK(status)) { goto failed; } @@ -796,7 +807,8 @@ NTSTATUS cldap_search_recv(struct tevent_req *req, *io->out.response = ldap_msg->r.SearchResultEntry; /* decode the 2nd part */ - status = ldap_decode(state->response.asn1, NULL, ldap_msg); + status = ldap_decode( + state->response.asn1, &limits, NULL, ldap_msg); if (!NT_STATUS_IS_OK(status)) { goto failed; } diff --git a/libcli/ldap/ldap_message.c b/libcli/ldap/ldap_message.c index f21598374a1..69a48279532 100644 --- a/libcli/ldap/ldap_message.c +++ b/libcli/ldap/ldap_message.c @@ -390,7 +390,7 @@ _PUBLIC_ bool ldap_encode(struct ldap_message *msg, const struct ldap_control_handler *control_handlers, DATA_BLOB *result, TALLOC_CTX *mem_ctx) { - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); int i, j; if (!data) return false; @@ -1162,6 +1162,7 @@ static bool ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data, /* This routine returns LDAP status codes */ _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, + const struct ldap_request_limits *limits, const struct ldap_control_handler *control_handlers, struct ldap_message *msg) { @@ -1258,7 +1259,11 @@ _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, struct ldap_SearchRequest *r = &msg->r.SearchRequest; int sizelimit, timelimit; const char **attrs = NULL; + size_t request_size = asn1_get_length(data); msg->type = LDAP_TAG_SearchRequest; + if (request_size > limits->max_search_size) { + goto prot_err; + } if (!asn1_start_tag(data, tag)) goto prot_err; if (!asn1_read_OctetString_talloc(msg, data, &r->basedn)) goto prot_err; if (!asn1_read_enumerated(data, (int *)(void *)&(r->scope))) goto prot_err; diff --git a/libcli/ldap/ldap_message.h b/libcli/ldap/ldap_message.h index 2f64881c053..19bfb99ac97 100644 --- a/libcli/ldap/ldap_message.h +++ b/libcli/ldap/ldap_message.h @@ -213,10 +213,15 @@ struct ldap_control_handler { bool (*encode)(void *mem_ctx, void *in, DATA_BLOB *out); }; +struct ldap_request_limits { + unsigned max_search_size; +}; + struct asn1_data; struct ldap_message *new_ldap_message(TALLOC_CTX *mem_ctx); NTSTATUS ldap_decode(struct asn1_data *data, + const struct ldap_request_limits *limits, const struct ldap_control_handler *control_handlers, struct ldap_message *msg); bool ldap_encode(struct ldap_message *msg, diff --git a/libcli/ldap/tests/data/10000-or.dat b/libcli/ldap/tests/data/10000-or.dat Binary files differnew file mode 100644 index 00000000000..e2d6de2ce33 --- /dev/null +++ b/libcli/ldap/tests/data/10000-or.dat diff --git a/libcli/ldap/tests/data/ldap-recursive.dat b/libcli/ldap/tests/data/ldap-recursive.dat Binary files differnew file mode 100644 index 00000000000..dd18d857660 --- /dev/null +++ b/libcli/ldap/tests/data/ldap-recursive.dat diff --git a/libcli/ldap/tests/ldap_message_test.c b/libcli/ldap/tests/ldap_message_test.c new file mode 100644 index 00000000000..c5aacd4bc6b --- /dev/null +++ b/libcli/ldap/tests/ldap_message_test.c @@ -0,0 +1,287 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + */ + +/* + * from cmocka.c: + * These headers or their equivalents should be included prior to + * including + * this header file. + * + * #include <stdarg.h> + * #include <stddef.h> + * #include <setjmp.h> + * + * This allows test applications to use custom definitions of C standard + * library functions and types. + * + */ +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> + +#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 + * coresponding 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[0]; + 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 containg 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; + 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)); +} + +/* + * 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; + 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); +} + +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_set_message_output(CM_OUTPUT_SUBUNIT); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/libcli/ldap/wscript_build b/libcli/ldap/wscript_build index db5b1df497a..d0aa7c11ced 100644 --- a/libcli/ldap/wscript_build +++ b/libcli/ldap/wscript_build @@ -6,3 +6,18 @@ bld.SAMBA_LIBRARY('cli-ldap-common', private_headers='ldap_message.h ldap_errors.h ldap_ndr.h', deps='samba-util asn1util NDR_SECURITY tevent', private_library=True) + +bld.SAMBA_BINARY( + 'test_ldap_message', + source='tests/ldap_message_test.c', + deps=''' + cmocka + talloc + ldb + samba-util + asn1util + NDR_SECURITY + cli-ldap + ''', + install=False +) diff --git a/python/samba/tests/ldap_raw.py b/python/samba/tests/ldap_raw.py new file mode 100644 index 00000000000..334fabce230 --- /dev/null +++ b/python/samba/tests/ldap_raw.py @@ -0,0 +1,234 @@ +# Integration tests for the ldap server, using raw socket IO +# +# Tests for handling of malformed or large packets. +# +# 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 <http://www.gnu.org/licenses/>. +# + +import socket + +import samba.tests +from samba.tests import TestCase + + +# +# LDAP Operations +# +SEARCH = b'\x63' + +EQUALS = b'\xa3' + + +# +# ASN.1 Element types +# +BOOLEAN = b'\x01' +INTEGER = b'\x02' +OCTET_STRING = b'\x04' +NULL = b'\x05' +ENUMERATED = b'\x0a' +SEQUENCE = b'\x30' +SET = b'\x31' + + +# +# ASN.1 Helper functions. +# +def encode_element(ber_type, data): + ''' Encode an ASN.1 BER element. ''' + if data is None: + return ber_type + encode_length(0) + return ber_type + encode_length(len(data)) + data + + +def encode_length(length): + ''' Encode the length of an ASN.1 BER element. ''' + + if length > 0xFFFFFF: + return b'\x84' + length.to_bytes(4, "big") + if length > 0xFFFF: + return b'\x83' + length.to_bytes(3, "big") + if length > 0xFF: + return b'\x82' + length.to_bytes(2, "big") + if length > 0x7F: + return b'\x81' + length.to_bytes(1, "big") + return length.to_bytes(1, "big") + + +def encode_string(string): + ''' Encode an octet string ''' + return encode_element(OCTET_STRING, string) + + +def encode_boolean(boolean): + ''' Encode a boolean value ''' + if boolean: + return encode_element(BOOLEAN, b'\xFF') + return encode_element(BOOLEAN, b'\x00') + + +def encode_integer(integer): + ''' Encode an integer value ''' + bit_len = integer.bit_length() + byte_len = (bit_len // 8) + 1 + return encode_element(INTEGER, integer.to_bytes(byte_len, "big")) + + +def encode_enumerated(enum): + ''' Encode an enumerated value ''' + return encode_element(ENUMERATED, enum.to_bytes(1, "big")) + + +def encode_sequence(sequence): + ''' Encode a sequence ''' + return encode_element(SEQUENCE, sequence) + + +class RawLdapTest(TestCase): + """A raw Ldap Test case.""" + + def setUp(self): + super(RawLdapTest, self).setUp() + + self.host = samba.tests.env_get_var_value('SERVER') + self.port = 389 + self.socket = None + self.connect() + + def tearDown(self): + self.disconnect() + super(RawLdapTest, self).tearDown() + + def disconnect(self): + ''' Disconnect from and clean up the connection to the server ''' + if self.socket is None: + return + self.socket.close() + self.socket = None + + def connect(self): + ''' Open a socket stream connection to the server ''' + try: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.settimeout(10) + self.socket.connect((self.host, self.port)) + except socket.error: + self.socket.close() + raise + + def send(self, req): + ''' Send the request to the server ''' + try: + self.socket.sendall(req) + except socket.error: + self.disconnect() + raise + + def recv(self, num_recv=0xffff, timeout=None): + ''' recv an array of bytes from the server ''' + data = None + try: + if timeout is not None: + self.socket.settimeout(timeout) + data = self.socket.recv(num_recv, 0) + self.socket.settimeout(10) + if len(data) == 0: + self.disconnect() + return None + except socket.timeout: + # We ignore timeout's as the ldap server will drop the connection + # on the errors we're testing. So returning None on a timeout is + # the desired behaviour. + self.socket.settimeout(10) + except socket.error: + self.disconnect() + raise + return data + + def test_search_equals_maximum_permitted_size(self): + ''' + Check that an LDAP search request equal to the maximum size is accepted + ''' + + # Lets build an ldap search packet to query the RootDSE + header = encode_string(None) # Base DN, "" + header += encode_enumerated(0) # Enumeration scope + header += encode_enumerated(0) # Enumeration dereference + header += encode_integer(0) # Integer size limit + header += encode_integer(0) # Integer time limit + header += encode_boolean(False) # Boolean attributes only + + # + # build an equality search of the form x...x=y...y + # With the length of x...x and y...y chosen to generate an + # ldap request of 256000 bytes. + x = encode_string(b'x' * 127974) + y = encode_string(b'y' * 127979) + equals = encode_element(EQUALS, x + y) + trailer = encode_sequence(None) + search = encode_element(SEARCH, header + equals + trailer) + + msg_no = encode_integer(1) + packet = encode_sequence(msg_no + search) + # + # The length of the packet should be equal to the + # Maximum length of a search query + self.assertEqual(256000, len(packet)) + + self.send(packet) + data = self.recv() + self.assertIsNotNone(data) + + # Should be a sequence + self.assertEqual(SEQUENCE, data[0:1]) + + def test_search_exceeds_maximum_permitted_size(self): + ''' + Test that a search query longer than the maximum permitted + size is rejected. + ''' + + # Lets build an ldap search packet to query the RootDSE + header = encode_string(None) # Base DN, "" + header += encode_enumerated(0) # Enumeration scope + header += encode_enumerated(0) # Enumeration dereference + header += encode_integer(0) # Integer size limit + header += encode_integer(0) # Integer time limit + header += encode_boolean(False) # Boolean attributes only + + # + # build an equality search of the form x...x=y...y + # With the length of x...x and y...y chosen to generate an + # ldap request of 256001 bytes. + x = encode_string(b'x' * 127979) + y = encode_string(b'y' * 127975) + equals = encode_element(EQUALS, x + y) + trailer = encode_sequence(None) + search = encode_element(SEARCH, header + equals + trailer) + + msg_no = encode_integer(1) + packet = encode_sequence(msg_no + search) + # + # The length of the sequence data should be one greater than the + # Maximum length of a search query + self.assertEqual(256001, len(packet)) + + self.send(packet) + data = self.recv() + # + # The connection should be closed by the server and we should not + # see any data. + self.assertIsNone(data) diff --git a/source3/lib/tldap.c b/source3/lib/tldap.c index d6c6e8859a6..bf5fc05d785 100644 --- a/source3/lib/tldap.c +++ b/source3/lib/tldap.c @@ -632,7 +632,7 @@ static void tldap_msg_received(struct tevent_req *subreq) goto fail; } - data = asn1_init(talloc_tos()); + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (data == NULL) { status = TLDAP_NO_MEMORY; goto fail; @@ -763,7 +763,7 @@ static struct tevent_req *tldap_req_create(TALLOC_CTX *mem_ctx, if (req == NULL) { return NULL; } - state->out = asn1_init(state); + state->out = asn1_init(state, ASN1_MAX_TREE_DEPTH); if (state->out == NULL) { goto err; } diff --git a/source3/lib/tldap_util.c b/source3/lib/tldap_util.c index 1b86962a32e..168932a8a96 100644 --- a/source3/lib/tldap_util.c +++ b/source3/lib/tldap_util.c @@ -644,7 +644,7 @@ static struct tevent_req *tldap_ship_paged_search( struct tldap_control *pgctrl; struct asn1_data *asn1 = NULL; - asn1 = asn1_init(state); + asn1 = asn1_init(state, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { return NULL; } @@ -783,7 +783,7 @@ static void tldap_search_paged_done(struct tevent_req *subreq) TALLOC_FREE(state->cookie.data); - asn1 = asn1_init(talloc_tos()); + asn1 = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (tevent_req_nomem(asn1, req)) { return; } diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c index 4a0fbcd73af..1608f6a9960 100644 --- a/source3/libsmb/clispnego.c +++ b/source3/libsmb/clispnego.c @@ -50,7 +50,7 @@ bool spnego_parse_negTokenInit(TALLOC_CTX *ctx, *secblob = data_blob_null; } - data = asn1_init(talloc_tos()); + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (data == NULL) { return false; } @@ -171,7 +171,7 @@ DATA_BLOB spnego_gen_krb5_wrap(TALLOC_CTX *ctx, const DATA_BLOB ticket, const ui ASN1_DATA *data; DATA_BLOB ret = data_blob_null; - data = asn1_init(talloc_tos()); + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (data == NULL) { return data_blob_null; } diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index a8d5fdc5954..5ee6d33c7fc 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -956,6 +956,10 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals) Globals.prefork_backoff_increment = 10; Globals.prefork_maximum_backoff = 120; + Globals.ldap_max_anonymous_request_size = 256000; + Globals.ldap_max_authenticated_request_size = 16777216; + Globals.ldap_max_search_request_size = 256000; + /* Now put back the settings that were set with lp_set_cmdline() */ apply_lp_set_cmdline(); } diff --git a/source3/torture/torture.c b/source3/torture/torture.c index a795e61125f..c4b0a7bc4f9 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -11370,7 +11370,7 @@ tldap_build_extended_control(enum tldap_extended_val val) ZERO_STRUCT(empty_control); if (val != EXTENDED_NONE) { - data = asn1_init(talloc_tos()); + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); if (!data) { return NULL; diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c index 0323da87d29..b735063656a 100644 --- a/source4/auth/gensec/gensec_krb5.c +++ b/source4/auth/gensec/gensec_krb5.c @@ -444,7 +444,7 @@ static DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLO struct asn1_data *data; DATA_BLOB ret = data_blob_null; - data = asn1_init(mem_ctx); + data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data || !ticket->data) { return ret; } @@ -478,7 +478,7 @@ static DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLO static bool gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2]) { bool ret = false; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); int data_remaining; if (!data) { diff --git a/source4/dsdb/samdb/ldb_modules/paged_results.c b/source4/dsdb/samdb/ldb_modules/paged_results.c index 940d2254fb0..dc211dd18ce 100644 --- a/source4/dsdb/samdb/ldb_modules/paged_results.c +++ b/source4/dsdb/samdb/ldb_modules/paged_results.c @@ -483,8 +483,14 @@ paged_results_copy_down_controls(TALLOC_CTX *mem_ctx, if (control->oid == NULL) { continue; } - if (strncmp(control->oid, LDB_CONTROL_PAGED_RESULTS_OID, - sizeof(LDB_CONTROL_PAGED_RESULTS_OID)) == 0) { + if (strcmp(control->oid, LDB_CONTROL_PAGED_RESULTS_OID) == 0) { + continue; + } + /* + * ASQ changes everything, do not copy it down for the + * per-GUID search + */ + if (strcmp(control->oid, LDB_CONTROL_ASQ_OID) == 0) { continue; } new_controls[j] = talloc_steal(new_controls, control); @@ -534,21 +540,23 @@ static bool paged_controls_same(struct ldb_request *req, num_non_null_req_controls = 0; for (i=0; req->controls[i] != NULL; i++) { - if (req->controls[i]->oid != NULL) { + if (req->controls[i]->oid != NULL && + strcmp(req->controls[i]->oid, + LDB_CONTROL_ASQ_OID) != 0) { num_non_null_req_controls++; } } /* At this point we have the number of non-null entries for both * control lists and we know that: - * 1. down_controls does not contain the paged control + * 1. down_controls does not contain the paged control or ASQ * (because paged_results_copy_down_controls excludes it) * 2. req->controls does contain the paged control * (because this function is only called if this is true) * 3. down_controls is a subset of non-null controls in req->controls * (checked above) * So to confirm that the two lists are identical except for the paged - * control, all we need to check is: */ + * control and possibly ASQ, all we need to check is: */ if (num_non_null_req_controls == num_down_controls + 1) { return true; } diff --git a/source4/dsdb/tests/python/asq.py b/source4/dsdb/tests/python/asq.py new file mode 100644 index 00000000000..a32c9f40cd3 --- /dev/null +++ b/source4/dsdb/tests/python/asq.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# +# Test ASQ LDAP control behaviour in Samba +# Copyright (C) Andrew Bartlett 2019-2020 +# +# Based on Unit tests for the notification control +# Copyright (C) Stefan Metzmacher 2016 +# +# 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 <http://www.gnu.org/licenses/>. + +import optparse +import sys +import os +import random + +sys.path.insert(0, "bin/python") +import samba +from samba.tests.subunitrun import SubunitOptions, TestProgram + +import samba.getopt as options + +from samba.auth import system_session +from samba import ldb +from samba.samdb import SamDB +from samba.ndr import ndr_unpack +from samba import gensec +from samba.credentials import Credentials +import samba.tests + +from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError +from ldb import ERR_TIME_LIMIT_EXCEEDED, ERR_ADMIN_LIMIT_EXCEEDED, ERR_UNWILLING_TO_PERFORM +from ldb import Message + +parser = optparse.OptionParser("large_ldap.py [options] <host>") +sambaopts = options.SambaOptions(parser) +parser.add_option_group(sambaopts) +parser.add_option_group(options.VersionOptions(parser)) +# use command line creds if available +credopts = options.CredentialsOptions(parser) +parser.add_option_group(credopts) +subunitopts = SubunitOptions(parser) +parser.add_option_group(subunitopts) +opts, args = parser.parse_args() + +if len(args) < 1: + parser.print_usage() + sys.exit(1) + +url = args[0] + +lp = sambaopts.get_loadparm() +creds = credopts.get_credentials(lp) + + +class ASQLDAPTest(samba.tests.TestCase): + + def setUp(self): + super(ASQLDAPTest, self).setUp() + self.ldb = samba.Ldb(url, credentials=creds, session_info=system_session(lp), lp=lp) + self.base_dn = self.ldb.get_default_basedn() + self.NAME_ASQ="asq_" + format(random.randint(0, 99999), "05") + self.OU_NAME_ASQ= self.NAME_ASQ + "_ou" + self.ou_dn = ldb.Dn(self.ldb, "ou=" + self.OU_NAME_ASQ + "," + str(self.base_dn)) + + samba.tests.delete_force(self.ldb, self.ou_dn, + controls=['tree_delete:1']) + + self.ldb.add({ + "dn": self.ou_dn, + "objectclass": "organizationalUnit", + "ou": self.OU_NAME_ASQ}) + + self.members = [] + self.members2 = [] + + for x in range(20): + name = self.NAME_ASQ + "_" + str(x) + dn = ldb.Dn(self.ldb, + "cn=" + name + "," + str(self.ou_dn)) + self.members.append(dn) + self.ldb.add({ + "dn": dn, + "objectclass": "group"}) + + for x in range(20): + name = self.NAME_ASQ + "_" + str(x + 20) + dn = ldb.Dn(self.ldb, + "cn=" + name + "," + str(self.ou_dn)) + self.members2.append(dn) + self.ldb.add({ + "dn": dn, + "objectclass": "group", + "member": [str(x) for x in self.members]}) + + name = self.NAME_ASQ + "_" + str(x + 40) + self.top_dn = ldb.Dn(self.ldb, + "cn=" + name + "," + str(self.ou_dn)) + self.ldb.add({ + "dn": self.top_dn, + "objectclass": "group", + "member": [str(x) for x in self.members2]}) + + def tearDown(self): + samba.tests.delete_force(self.ldb, self.ou_dn, + controls=['tree_delete:1']) + + def test_asq(self): + """Testing ASQ behaviour. + + ASQ is very strange, it turns a BASE search into a search for + all the objects pointed to by the specified attribute, + returning multiple entries! + + """ + + msgs = self.ldb.search(base=self.top_dn, + scope=ldb.SCOPE_BASE, + attrs=["objectGUID", "cn", "member"], + controls=["asq:1:member"]) + + self.assertEqual(len(msgs), 20) + + for msg in msgs: + self.assertNotEqual(msg.dn, self.top_dn) + self.assertIn(msg.dn, self.members2) + for group in msg["member"]: + self.assertIn(ldb.Dn(self.ldb, str(group)), + self.members) + + def test_asq_paged(self): + """Testing ASQ behaviour with paged_results set. + + ASQ is very strange, it turns a BASE search into a search for + all the objects pointed to by the specified attribute, + returning multiple entries! + + """ + + msgs = self.ldb.search(base=self.top_dn, + scope=ldb.SCOPE_BASE, + attrs=["objectGUID", "cn", "member"], + controls=["asq:1:member", + "paged_results:1:1024"]) + + self.assertEqual(len(msgs), 20) + + for msg in msgs: + self.assertNotEqual(msg.dn, self.top_dn) + self.assertIn(msg.dn, self.members2) + for group in msg["member"]: + self.assertIn(ldb.Dn(self.ldb, str(group)), + self.members) + +if "://" not in url: + if os.path.isfile(url): + url = "tdb://%s" % url + else: + url = "ldap://%s" % url + +TestProgram(module=__name__, opts=subunitopts) diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index 709b7bcacfa..a9b162b284e 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -441,6 +441,10 @@ static void ldapsrv_accept_tls_done(struct tevent_req *subreq) } static void ldapsrv_call_read_done(struct tevent_req *subreq); +static NTSTATUS ldapsrv_packet_check( + void *private_data, + DATA_BLOB blob, + size_t *packet_size); static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn) { @@ -494,7 +498,7 @@ static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn) conn->connection->event.ctx, conn->sockets.active, 7, /* initial_read_size */ - ldap_full_packet, + ldapsrv_packet_check, conn); if (subreq == NULL) { ldapsrv_terminate_connection(conn, "ldapsrv_call_read_next: " @@ -520,6 +524,9 @@ static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn) } static void ldapsrv_call_process_done(struct tevent_req *subreq); +static int ldapsrv_check_packet_size( + struct ldapsrv_connection *conn, + size_t size); static void ldapsrv_call_read_done(struct tevent_req *subreq) { @@ -530,6 +537,8 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq) struct ldapsrv_call *call; struct asn1_data *asn1; DATA_BLOB blob; + int ret = LDAP_SUCCESS; + struct ldap_request_limits limits = {0}; conn->sockets.read_req = NULL; @@ -560,7 +569,15 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq) return; } - asn1 = asn1_init(call); + ret = ldapsrv_check_packet_size(conn, blob.length); + if (ret != LDAP_SUCCESS) { + ldapsrv_terminate_connection( + conn, + "Request packet too large"); + return; + } + + asn1 = asn1_init(call, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { ldapsrv_terminate_connection(conn, "no memory"); return; @@ -577,8 +594,13 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq) return; } - status = ldap_decode(asn1, samba_ldap_control_handlers(), - call->request); + limits.max_search_size = + lpcfg_ldap_max_search_request_size(conn->lp_ctx); + status = ldap_decode( + asn1, + &limits, + samba_ldap_control_handlers(), + call->request); if (!NT_STATUS_IS_OK(status)) { ldapsrv_terminate_connection(conn, nt_errstr(status)); return; @@ -1362,6 +1384,84 @@ static void ldapsrv_post_fork(struct task_server *task, struct process_details * } } +/* + * Check the size of an ldap request packet. + * + * For authenticated connections the maximum packet size is controlled by + * the smb.conf parameter "ldap max authenticated request size" + * + * For anonymous connections the maximum packet size is controlled by + * the smb.conf parameter "ldap max anonymous request size" + */ +static int ldapsrv_check_packet_size( + struct ldapsrv_connection *conn, + size_t size) +{ + bool is_anonymous = false; + size_t max_size = 0; + + max_size = lpcfg_ldap_max_anonymous_request_size(conn->lp_ctx); + if (size <= max_size) { + return LDAP_SUCCESS; + } + + /* + * Request is larger than the maximum unauthenticated request size. + * As this code is called frequently we avoid calling + * security_token_is_anonymous if possible + */ + if (conn->session_info != NULL && + conn->session_info->security_token != NULL) { + is_anonymous = security_token_is_anonymous( + conn->session_info->security_token); + } + + if (is_anonymous) { + DBG_WARNING( + "LDAP request size (%zu) exceeds (%zu)\n", + size, + max_size); + return LDAP_UNWILLING_TO_PERFORM; + } + + max_size = lpcfg_ldap_max_authenticated_request_size(conn->lp_ctx); + if (size > max_size) { + DBG_WARNING( + "LDAP request size (%zu) exceeds (%zu)\n", + size, + max_size); + return LDAP_UNWILLING_TO_PERFORM; + } + return LDAP_SUCCESS; + +} + +/* + * Check that the blob contains enough data to be a valid packet + * If there is a packet header check the size to ensure that it does not + * exceed the maximum sizes. + * + */ +static NTSTATUS ldapsrv_packet_check( + void *private_data, + DATA_BLOB blob, + size_t *packet_size) +{ + NTSTATUS ret; + struct ldapsrv_connection *conn = private_data; + int result = LDB_SUCCESS; + + ret = ldap_full_packet(private_data, blob, packet_size); + if (!NT_STATUS_IS_OK(ret)) { + return ret; + } + result = ldapsrv_check_packet_size(conn, *packet_size); + if (result != LDAP_SUCCESS) { + return NT_STATUS_LDAP(result); + } + return NT_STATUS_OK; +} + NTSTATUS server_service_ldap_init(TALLOC_CTX *ctx) { static const struct service_details details = { diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index da84adc7769..cf276679594 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -277,6 +277,7 @@ static void ldap_connection_recv_done(struct tevent_req *subreq) struct ldap_message *msg; struct asn1_data *asn1; DATA_BLOB blob; + struct ldap_request_limits limits = {0}; msg = talloc_zero(conn, struct ldap_message); if (msg == NULL) { @@ -284,7 +285,7 @@ static void ldap_connection_recv_done(struct tevent_req *subreq) return; } - asn1 = asn1_init(conn); + asn1 = asn1_init(conn, ASN1_MAX_TREE_DEPTH); if (asn1 == NULL) { TALLOC_FREE(msg); ldap_error_handler(conn, NT_STATUS_NO_MEMORY); @@ -306,7 +307,7 @@ static void ldap_connection_recv_done(struct tevent_req *subreq) asn1_load_nocopy(asn1, blob.data, blob.length); - status = ldap_decode(asn1, samba_ldap_control_handlers(), msg); + status = ldap_decode(asn1, &limits, samba_ldap_control_handlers(), msg); asn1_free(asn1); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(msg); diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c index 716ca148308..df012a158e0 100644 --- a/source4/libcli/ldap/ldap_controls.c +++ b/source4/libcli/ldap/ldap_controls.c @@ -32,7 +32,7 @@ static bool decode_server_sort_response(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB attr; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_sort_resp_control *lsrc; if (!data) return false; @@ -79,7 +79,7 @@ static bool decode_server_sort_request(void *mem_ctx, DATA_BLOB in, void *_out) void **out = (void **)_out; DATA_BLOB attr; DATA_BLOB rule; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_server_sort_control **lssc; int num; @@ -166,7 +166,7 @@ static bool decode_extended_dn_request(void *mem_ctx, DATA_BLOB in, void *_out) return true; } - data = asn1_init(mem_ctx); + data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; if (!asn1_load(data, in)) { @@ -198,7 +198,7 @@ static bool decode_extended_dn_request(void *mem_ctx, DATA_BLOB in, void *_out) static bool decode_sd_flags_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_sd_flags_control *lsdfc; if (!data) return false; @@ -232,7 +232,7 @@ static bool decode_sd_flags_request(void *mem_ctx, DATA_BLOB in, void *_out) static bool decode_search_options_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_search_options_control *lsoc; if (!data) return false; @@ -267,7 +267,7 @@ static bool decode_paged_results_request(void *mem_ctx, DATA_BLOB in, void *_out { void **out = (void **)_out; DATA_BLOB cookie; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_paged_control *lprc; if (!data) return false; @@ -316,7 +316,7 @@ static bool decode_dirsync_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB cookie; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_dirsync_control *ldc; if (!data) return false; @@ -372,7 +372,7 @@ static bool decode_asq_control(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB source_attribute; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_asq_control *lac; if (!data) return false; @@ -433,7 +433,7 @@ static bool decode_verify_name_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB name; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_verify_name_control *lvnc; int len; @@ -485,7 +485,7 @@ static bool decode_verify_name_request(void *mem_ctx, DATA_BLOB in, void *_out) static bool encode_verify_name_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_verify_name_control *lvnc = talloc_get_type(in, struct ldb_verify_name_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); DATA_BLOB gc_utf16; if (!data) return false; @@ -528,7 +528,7 @@ static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB assertion_value, context_id; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_vlv_req_control *lvrc; if (!data) return false; @@ -626,7 +626,7 @@ static bool decode_vlv_response(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; DATA_BLOB context_id; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct ldb_vlv_resp_control *lvrc; if (!data) return false; @@ -682,7 +682,7 @@ static bool decode_vlv_response(void *mem_ctx, DATA_BLOB in, void *_out) static bool encode_server_sort_response(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_sort_resp_control *lsrc = talloc_get_type(in, struct ldb_sort_resp_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -716,7 +716,7 @@ static bool encode_server_sort_response(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_server_sort_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_server_sort_control **lssc = talloc_get_type(in, struct ldb_server_sort_control *); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); int num; if (!data) return false; @@ -782,7 +782,7 @@ static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out) return true; } - data = asn1_init(mem_ctx); + data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -810,7 +810,7 @@ static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_sd_flags_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_sd_flags_control *lsdfc = talloc_get_type(in, struct ldb_sd_flags_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -838,7 +838,7 @@ static bool encode_sd_flags_request(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_search_options_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_search_options_control *lsoc = talloc_get_type(in, struct ldb_search_options_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -866,7 +866,7 @@ static bool encode_search_options_request(void *mem_ctx, void *in, DATA_BLOB *ou static bool encode_paged_results_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_paged_control *lprc = talloc_get_type(in, struct ldb_paged_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -901,7 +901,7 @@ static bool encode_paged_results_request(void *mem_ctx, void *in, DATA_BLOB *out static bool encode_asq_control(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_asq_control *lac = talloc_get_type(in, struct ldb_asq_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -936,7 +936,7 @@ static bool encode_asq_control(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_dirsync_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_dirsync_control *ldc = talloc_get_type(in, struct ldb_dirsync_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -972,7 +972,7 @@ static bool encode_dirsync_request(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_vlv_req_control *lvrc = talloc_get_type(in, struct ldb_vlv_req_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -1040,7 +1040,7 @@ static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out) static bool encode_vlv_response(void *mem_ctx, void *in, DATA_BLOB *out) { struct ldb_vlv_resp_control *lvrc = talloc_get_type(in, struct ldb_vlv_resp_control); - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -1083,7 +1083,7 @@ static bool encode_openldap_dereference(void *mem_ctx, void *in, DATA_BLOB *out) { struct dsdb_openldap_dereference_control *control = talloc_get_type(in, struct dsdb_openldap_dereference_control); int i,j; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); if (!data) return false; @@ -1132,7 +1132,7 @@ static bool encode_openldap_dereference(void *mem_ctx, void *in, DATA_BLOB *out) static bool decode_openldap_dereference(void *mem_ctx, DATA_BLOB in, void *_out) { void **out = (void **)_out; - struct asn1_data *data = asn1_init(mem_ctx); + struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH); struct dsdb_openldap_dereference_result_control *control; struct dsdb_openldap_dereference_result **r = NULL; int i = 0; diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index ae2b10ae659..f7645365384 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -881,10 +881,16 @@ plantestsuite_loadlist("samba4.ldap_modify_order.normal_user.python(ad_dc_defaul '$LOADLIST', '$LISTOPT']) +planoldpythontestsuite("ad_dc", + "samba.tests.ldap_raw", + extra_args=['-U"$USERNAME%$PASSWORD"'], + environ={'TEST_ENV': 'ad_dc'}) + plantestsuite_loadlist("samba4.tokengroups.krb5.python(ad_dc_default)", "ad_dc_default:local", [python, os.path.join(DSDB_PYTEST_DIR, "token_group.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '-k', 'yes', '$LOADLIST', '$LISTOPT']) plantestsuite_loadlist("samba4.tokengroups.ntlm.python(ad_dc_default)", "ad_dc_default:local", [python, os.path.join(DSDB_PYTEST_DIR, "token_group.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '-k', 'no', '$LOADLIST', '$LISTOPT']) plantestsuite("samba4.sam.python(fl2008r2dc)", "fl2008r2dc", [python, os.path.join(DSDB_PYTEST_DIR, "sam.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) plantestsuite("samba4.sam.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "sam.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) +plantestsuite("samba4.asq.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "asq.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) plantestsuite("samba4.user_account_control.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "user_account_control.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) for env in ['ad_dc_default:local', 'schema_dc:local']: @@ -1321,6 +1327,8 @@ plantestsuite("samba4.dcerpc.dnsserver.dnsutils", "none", [os.path.join(bindir(), "test_rpc_dns_server_dnsutils")]) plantestsuite("libcli.drsuapi.repl_decrypt", "none", [os.path.join(bindir(), "test_repl_decrypt")]) +plantestsuite("libcli.ldap.ldap_message", "none", + [os.path.join(bindir(), "test_ldap_message")]) # process restart and limit tests, these break the environment so need to run # in their own specific environment |