The branch, v4-17-test has been updated via 0b562285733 VERSION: Bump version up to Samba 4.17.3... via 21f995104c8 VERSION: Disable GIT_SNAPSHOT for the 4.17.2 release. via 37fa752e978 WHATSNEW: Add release notes for Samba 4.17.2. via e96d28093ae CVE-2022-3592 smbd: Slightly simplify filename_convert_dirfsp() via ace0ebde325 CVE-2022-3592 lib: add subdir_of() to source3/lib/util_path.c via 4e3e3f9c4fe CVE-2022-3592 torture3: Show that our symlink traversal checks are insecure via 4fbcfb285a9 CVE-2022-3592 smbd: No empty path components in openat_pathref_dirfsp_nosymlink() via 3007e32072f CVE-2022-3437 third_party/heimdal: Pass correct length to _gssapi_verify_pad() via f33f8a515b0 CVE-2022-3437 third_party/heimdal: Check for overflow in _gsskrb5_get_mech() via 0de566954ec CVE-2022-3437 third_party/heimdal: Check buffer length against overflow for DES{,3} unwrap via a0cd16f084d CVE-2022-3437 third_party/heimdal: Check the result of _gsskrb5_get_mech() via c06f2e9ce24 CVE-2022-3437 third_party/heimdal: Avoid undefined behaviour in _gssapi_verify_pad() via 24099e34819 CVE-2022-3437 third_party/heimdal: Don't pass NULL pointers to memcpy() in DES unwrap via abb3f7f1e3e CVE-2022-3437 third_party/heimdal: Use constant-time memcmp() in unwrap_des3() via 2ee62a7c9ff CVE-2022-3437 third_party/heimdal: Use constant-time memcmp() for arcfour unwrap via 846fbd0456a CVE-2022-3437 s4/auth/tests: Add unit tests for unwrap_des3() via d5a06cd54e0 CVE-2022-3437 third_party/heimdal_build: Add gssapi-subsystem subsystem via 16ea178f162 CVE-2022-3437 third_party/heimdal: Remove __func__ compatibility workaround from 96e8adf7ae9 VERSION: Bump version up to Samba 4.17.2...
https://git.samba.org/?p=samba.git;a=shortlog;h=v4-17-test - Log ----------------------------------------------------------------- commit 0b5622857339b9c2c50677c6b823eaa0f7f7fbf5 Author: Jule Anger <jan...@samba.org> Date: Tue Oct 25 11:47:31 2022 +0200 VERSION: Bump version up to Samba 4.17.3... and re-enable GIT_SNAPSHOT. Signed-off-by: Jule Anger <jan...@samba.org> ----------------------------------------------------------------------- Summary of changes: VERSION | 2 +- WHATSNEW.txt | 53 +- selftest/tests.py | 5 + source3/lib/util_path.c | 51 + source3/lib/util_path.h | 4 + .../script/tests/test_symlink_traversal_smb2.sh | 4 + source3/smbd/filename.c | 11 +- source3/smbd/files.c | 6 + source4/auth/tests/heimdal_unwrap_des.c | 1244 ++++++++++++++++++++ source4/auth/wscript_build | 21 + third_party/heimdal/lib/gssapi/krb5/arcfour.c | 14 +- third_party/heimdal/lib/gssapi/krb5/decapsulate.c | 12 +- third_party/heimdal/lib/gssapi/krb5/unwrap.c | 34 +- third_party/heimdal/lib/krb5/krb5_locl.h | 4 - third_party/heimdal_build/wscript_build | 18 +- 15 files changed, 1446 insertions(+), 37 deletions(-) create mode 100644 source4/auth/tests/heimdal_unwrap_des.c Changeset truncated at 500 lines: diff --git a/VERSION b/VERSION index 8d324fa4c66..59521d4f944 100644 --- a/VERSION +++ b/VERSION @@ -25,7 +25,7 @@ ######################################################## SAMBA_VERSION_MAJOR=4 SAMBA_VERSION_MINOR=17 -SAMBA_VERSION_RELEASE=2 +SAMBA_VERSION_RELEASE=3 ######################################################## # If a official release has a serious bug # diff --git a/WHATSNEW.txt b/WHATSNEW.txt index 307c166a98e..9b9d644694d 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -1,3 +1,53 @@ + ============================== + Release Notes for Samba 4.17.2 + October 25, 2022 + ============================== + + +This is a security release in order to address the following defects: + +o CVE-2022-3437: There is a limited write heap buffer overflow in the GSSAPI + unwrap_des() and unwrap_des3() routines of Heimdal (included + in Samba). + https://www.samba.org/samba/security/CVE-2022-3437.html + +o CVE-2022-3592: A malicious client can use a symlink to escape the exported + directory. + https://www.samba.org/samba/security/CVE-2022-3592.html + +Changes since 4.17.1 +-------------------- + +o Volker Lendecke <v...@samba.org> + * BUG 15207: CVE-2022-3592. + +o Joseph Sutton <josephsut...@catalyst.net.nz> + * BUG 15134: CVE-2022-3437. + + +####################################### +Reporting bugs & Development Discussion +####################################### + +Please discuss this release on the samba-technical mailing list or by +joining the #samba-technical:matrix.org matrix room, or +#samba-technical IRC channel on irc.libera.chat. + +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.17.1 October 19, 2022 @@ -83,8 +133,7 @@ database (https://bugzilla.samba.org/). ====================================================================== -Release notes for older releases follow: ----------------------------------------- +---------------------------------------------------------------------- ============================== Release Notes for Samba 4.17.0 September 13, 2022 diff --git a/selftest/tests.py b/selftest/tests.py index b5e418cca3b..0b6ceb5fbb9 100644 --- a/selftest/tests.py +++ b/selftest/tests.py @@ -47,6 +47,8 @@ with_pam = ("WITH_PAM" in config_hash) with_elasticsearch_backend = ("HAVE_SPOTLIGHT_BACKEND_ES" in config_hash) pam_wrapper_so_path = config_hash.get("LIBPAM_WRAPPER_SO_PATH") pam_set_items_so_path = config_hash.get("PAM_SET_ITEMS_SO_PATH") +have_heimdal_support = "SAMBA4_USES_HEIMDAL" in config_hash +using_system_gssapi = "USING_SYSTEM_GSSAPI" in config_hash planpythontestsuite("none", "samba.tests.source") planpythontestsuite("none", "samba.tests.source_chars") @@ -448,6 +450,9 @@ plantestsuite("samba.unittests.test_oLschema2ldif", "none", [os.path.join(bindir(), "default/source4/utils/oLschema2ldif/test_oLschema2ldif")]) plantestsuite("samba.unittests.auth.sam", "none", [os.path.join(bindir(), "test_auth_sam")]) +if have_heimdal_support and not using_system_gssapi: + plantestsuite("samba.unittests.auth.heimdal_gensec_unwrap_des", "none", + [valgrindify(os.path.join(bindir(), "test_heimdal_gensec_unwrap_des"))]) if with_elasticsearch_backend: plantestsuite("samba.unittests.mdsparser_es", "none", [os.path.join(bindir(), "default/source3/test_mdsparser_es")] + [configuration]) diff --git a/source3/lib/util_path.c b/source3/lib/util_path.c index 3591589cb8e..b8dfec40b03 100644 --- a/source3/lib/util_path.c +++ b/source3/lib/util_path.c @@ -23,6 +23,7 @@ #include "replace.h" #include <talloc.h> +#include "lib/util/debug.h" #include "lib/util/samba_util.h" #include "lib/util_path.h" @@ -204,3 +205,53 @@ char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *pathname_in) *p++ = '\0'; return pathname; } + +/* + * Take two absolute paths, figure out if "subdir" is a proper + * subdirectory of "parent". Return the component relative to the + * "parent" without the potential "/". Take care of "parent" + * possibly ending in "/". + */ +bool subdir_of(const char *parent, + size_t parent_len, + const char *subdir, + const char **_relative) +{ + const char *relative = NULL; + bool matched; + + SMB_ASSERT(parent[0] == '/'); + SMB_ASSERT(subdir[0] == '/'); + + if (parent_len == 1) { + /* + * Everything is below "/" + */ + *_relative = subdir+1; + return true; + } + + if (parent[parent_len-1] == '/') { + parent_len -= 1; + } + + matched = (strncmp(subdir, parent, parent_len) == 0); + if (!matched) { + return false; + } + + relative = &subdir[parent_len]; + + if (relative[0] == '\0') { + *_relative = relative; /* nothing left */ + return true; + } + + if (relative[0] == '/') { + /* End of parent must match a '/' in subdir. */ + *_relative = relative+1; + return true; + } + + return false; +} diff --git a/source3/lib/util_path.h b/source3/lib/util_path.h index 3e7d04de550..0ea508bf5bb 100644 --- a/source3/lib/util_path.h +++ b/source3/lib/util_path.h @@ -31,5 +31,9 @@ char *lock_path(TALLOC_CTX *mem_ctx, const char *name); char *state_path(TALLOC_CTX *mem_ctx, const char *name); char *cache_path(TALLOC_CTX *mem_ctx, const char *name); char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path); +bool subdir_of(const char *parent, + size_t parent_len, + const char *subdir, + const char **_relative); #endif diff --git a/source3/script/tests/test_symlink_traversal_smb2.sh b/source3/script/tests/test_symlink_traversal_smb2.sh index 971d5344216..1fb9c67420d 100755 --- a/source3/script/tests/test_symlink_traversal_smb2.sh +++ b/source3/script/tests/test_symlink_traversal_smb2.sh @@ -144,6 +144,9 @@ chmod 0 "$dir_outside_share_noperms" ln -s "dir_inside_share_noperms" "symlink_dir_inside_share_noperms" mkdir "dir_inside_share_noperms/noperm_subdir_exists" touch "dir_inside_share_noperms/noperm_subdir_exists/noperm_subdir_file_exists" + + # Symlink pointing out of the share + ln -s "$share_test_dir"a"/etc" x ) # @@ -345,6 +348,7 @@ test_symlink_traversal_SMB2() smbclient_expect_error "get" "symlink_to_dir_exists/subdir_exists" "" "NT_STATUS_FILE_IS_A_DIRECTORY" || return 1 smbclient_expect_error "get" "symlink_to_dir_exists/subdir_exists/noexist1" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1 smbclient_expect_error "get" "symlink_to_dir_exists/subdir_exists/noexist1/noexist2" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1 + smbclient_expect_error "get" "x/passwd" "passwd" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1 # # Test paths within share with no permissions. diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 0be8e320ffa..e7873eb124f 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -1440,6 +1440,7 @@ NTSTATUS filename_convert_dirfsp( struct smb_filename **_smb_fname) { char *substitute = NULL; + const char *relative = NULL; size_t unparsed = 0; NTSTATUS status; char *target = NULL; @@ -1512,17 +1513,17 @@ next: DBG_DEBUG("abs_target_canon=%s\n", abs_target_canon); - in_share = strncmp( - abs_target_canon, + in_share = subdir_of( conn->connectpath, - strlen(conn->connectpath)) == 0; + strlen(conn->connectpath), + abs_target_canon, + &relative); if (!in_share) { DBG_DEBUG("wide link to %s\n", abs_target_canon); return NT_STATUS_OBJECT_PATH_NOT_FOUND; } - name_in = talloc_strdup( - mem_ctx, abs_target_canon + strlen(conn->connectpath) + 1); + name_in = talloc_strdup(mem_ctx, relative); symlink_redirects += 1; diff --git a/source3/smbd/files.c b/source3/smbd/files.c index 179c3e11a76..9b0c902c0d4 100644 --- a/source3/smbd/files.c +++ b/source3/smbd/files.c @@ -817,6 +817,12 @@ NTSTATUS openat_pathref_dirfsp_nosymlink( next = strv_next(path, rel_fname.base_name); + /* + * Path sanitizing further up has cleaned or rejected + * empty path components. Assert this here. + */ + SMB_ASSERT(rel_fname.base_name[0] != '\0'); + if (ISDOT(rel_fname.base_name) || ISDOTDOT(rel_fname.base_name)) { DBG_DEBUG("%s contains a dot\n", path_in); status = NT_STATUS_OBJECT_NAME_INVALID; diff --git a/source4/auth/tests/heimdal_unwrap_des.c b/source4/auth/tests/heimdal_unwrap_des.c new file mode 100644 index 00000000000..fbfe7782e7e --- /dev/null +++ b/source4/auth/tests/heimdal_unwrap_des.c @@ -0,0 +1,1244 @@ +/* + * Unit tests for third_party/heimdal/lib/gssapi/krb5/unwrap.c + * + * Copyright (C) Catalyst.NET Ltd 2022 + * + * 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 "includes.h" +#include "replace.h" + +#include "../../../third_party/heimdal/lib/gssapi/gssapi/gssapi.h" +#include "gsskrb5_locl.h" + +/****************************************************************************** + * Helper functions + ******************************************************************************/ + +const uint8_t *valid_range_begin; +const uint8_t *valid_range_end; +const uint8_t *invalid_range_end; + +/* + * 'array_len' is the size of the passed in array. 'buffer_len' is the size to + * report in the resulting buffer. + */ +static const gss_buffer_desc get_input_buffer(TALLOC_CTX *mem_ctx, + const uint8_t array[], + const size_t array_len, + const size_t buffer_len) +{ + gss_buffer_desc buf; + + /* Add some padding to catch invalid memory accesses. */ + const size_t padding = 0x100; + const size_t padded_len = array_len + padding; + + uint8_t *data = talloc_size(mem_ctx, padded_len); + assert_non_null(data); + + memcpy(data, array, array_len); + memset(data + array_len, 0, padding); + + assert_in_range(buffer_len, 0, array_len); + + buf.value = data; + buf.length = buffer_len; + + valid_range_begin = buf.value; + valid_range_end = valid_range_begin + buf.length; + invalid_range_end = valid_range_begin + padded_len; + + return buf; +} + +static void assert_mem_in_valid_range(const uint8_t *ptr, const size_t len) +{ + /* Ensure we've set up the range pointers properly. */ + assert_non_null(valid_range_begin); + assert_non_null(valid_range_end); + assert_non_null(invalid_range_end); + + /* + * Ensure the length isn't excessively large (a symptom of integer + * underflow). + */ + assert_in_range(len, 0, 0x1000); + + /* Ensure the memory is in our valid range. */ + assert_in_range(ptr, valid_range_begin, valid_range_end); + assert_in_range(ptr + len, valid_range_begin, valid_range_end); +} + +/* + * This function takes a pointer to volatile to allow it to be called from the + * ct_memcmp() wrapper. + */ +static void assert_mem_outside_invalid_range(const volatile uint8_t *ptr, + const size_t len) +{ + const LargestIntegralType _valid_range_end + = cast_ptr_to_largest_integral_type(valid_range_end); + const LargestIntegralType _invalid_range_end + = cast_ptr_to_largest_integral_type(invalid_range_end); + const LargestIntegralType _ptr = cast_ptr_to_largest_integral_type(ptr); + const LargestIntegralType _len = cast_to_largest_integral_type(len); + + /* Ensure we've set up the range pointers properly. */ + assert_non_null(valid_range_begin); + assert_non_null(valid_range_end); + assert_non_null(invalid_range_end); + + /* + * Ensure the length isn't excessively large (a symptom of integer + * underflow). + */ + assert_in_range(len, 0, 0x1000); + + /* Ensure the memory is outside the invalid range. */ + if (_ptr < _invalid_range_end && _ptr + _len > _valid_range_end) { + fail(); + } +} + +/***************************************************************************** + * wrapped functions + *****************************************************************************/ + +krb5_keyblock dummy_key; + +krb5_error_code __wrap_krb5_auth_con_getlocalsubkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock **keyblock); +krb5_error_code __wrap_krb5_auth_con_getlocalsubkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock **keyblock) +{ + *keyblock = &dummy_key; + return 0; +} + +void __wrap_krb5_free_keyblock(krb5_context context, + krb5_keyblock *keyblock); +void __wrap_krb5_free_keyblock(krb5_context context, + krb5_keyblock *keyblock) +{ + assert_ptr_equal(&dummy_key, keyblock); +} + +struct krb5_crypto_data dummy_crypto; + +krb5_error_code __wrap_krb5_crypto_init(krb5_context context, + const krb5_keyblock *key, + krb5_enctype etype, + krb5_crypto *crypto); +krb5_error_code __wrap_krb5_crypto_init(krb5_context context, + const krb5_keyblock *key, + krb5_enctype etype, + krb5_crypto *crypto) +{ + static const LargestIntegralType etypes[] = {ETYPE_DES3_CBC_NONE, 0}; + + assert_ptr_equal(&dummy_key, key); + assert_in_set(etype, etypes, ARRAY_SIZE(etypes)); + + *crypto = &dummy_crypto; + + return 0; +} + +krb5_error_code __wrap_krb5_decrypt(krb5_context context, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + krb5_data *result); +krb5_error_code __wrap_krb5_decrypt(krb5_context context, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + krb5_data *result) +{ + assert_ptr_equal(&dummy_crypto, crypto); + assert_int_equal(KRB5_KU_USAGE_SEAL, usage); + + assert_mem_in_valid_range(data, len); + + check_expected(len); + check_expected_ptr(data); + + result->data = malloc(len); + assert_non_null(result->data); + result->length = len; + + memcpy(result->data, data, len); + + return 0; +} + +krb5_error_code __wrap_krb5_decrypt_ivec(krb5_context context, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + krb5_data *result, + void *ivec); +krb5_error_code __wrap_krb5_decrypt_ivec(krb5_context context, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + assert_ptr_equal(&dummy_crypto, crypto); + assert_int_equal(KRB5_KU_USAGE_SEQ, usage); + + assert_mem_in_valid_range(data, len); + + assert_int_equal(8, len); + check_expected_ptr(data); + check_expected_ptr(ivec); -- Samba Shared Repository