diff options
Diffstat (limited to '')
135 files changed, 3881 insertions, 901 deletions
diff --git a/.gitlab-ci-coverage-runners.yml b/.gitlab-ci-coverage-runners.yml index 0f6b2ec..331c5d2 100644 --- a/.gitlab-ci-coverage-runners.yml +++ b/.gitlab-ci-coverage-runners.yml @@ -1,10 +1,4 @@ include: - /.gitlab-ci-default-runners.yml -.shared_runner_test: - # We need the more powerful n1-standard-2 runners - # in order to handle the lcov overhead. - # - # See .gitlab-ci-default-runners.yml for more details - tags: - - gitlab-org-docker +# Currently we're happy with the defaults diff --git a/.gitlab-ci-default-runners.yml b/.gitlab-ci-default-runners.yml index f73f868..bdc504a 100644 --- a/.gitlab-ci-default-runners.yml +++ b/.gitlab-ci-default-runners.yml @@ -1,48 +1,26 @@ -# From https://docs.gitlab.com/ee/user/gitlab_com/#shared-runners: +# From https://docs.gitlab.com/ee/ci/runners/hosted_runners/linux.html # # ... # -# All your CI/CD jobs run on n1-standard-1 instances with 3.75GB of RAM, CoreOS -# and the latest Docker Engine installed. Instances provide 1 vCPU and 25GB of -# HDD disk space. The default region of the VMs is US East1. Each instance is -# used only for one job, this ensures any sensitive data left on the system can’t -# be accessed by other people their CI jobs. -# -# The gitlab-shared-runners-manager-X.gitlab.com fleet of runners are dedicated -# for GitLab projects as well as community forks of them. They use a slightly -# larger machine type (n1-standard-2) and have a bigger SSD disk size. They don’t -# run untagged jobs and unlike the general fleet of shared runners, the instances -# are re-used up to 40 times. -# -# ... -# -# The n1-standard-1 runners seem to be tagged with 'docker' together with 'gce'. -# -# The more powerful n1-standard-2 runners seem to be tagged with -# 'gitlab-org-docker' or some with just 'gitlab-org'. -# +# Runner Tag vCPUs Memory Storage +# saas-linux-small-amd64 2 8 GB 25 GB # # Our current private runner 'docker', 'samba-ci-private', 'shared' and # 'ubuntu2204'. It runs with an ubuntu2204 kernel (5.15) and provides an -# ext4 filesystem and similar RAM as the n1-standard-2 runners. +# ext4 filesystem, 2 CPU and 4 GB (shared tag) 8G (samba-ci-private tag) RAM. # .shared_runner_build: - # We use n1-standard-1 shared runners by default. - # - # There are currently 5 shared runners with 'docker' and 'gce', - # while there are only 2 provising 'docker' together with 'shared'. + # We use saas-linux-small-amd64 shared runners by default. + # We avoid adding explicit tags for them in order + # to work with potential changes in future # - # We used to fallback to our private runner if the docker+shared runners - # were busy, but now that we use the 5 docker+gce runners, we try to only - # use shared runners without a fallback to our private runner! - # Lets see how that will work out. - tags: - - docker - - gce + # In order to generate valid yaml, we define a dummy variable... + variables: + SAMBA_SHARED_RUNNER_BUILD_DUMMY_VARIABLE: shared_runner_build .shared_runner_test: - # Currently we're fine using the n1-standard-1 runners also for testing + # We use saas-linux-small-amd64 shared runners by default. extends: .shared_runner_build .private_runner_test: diff --git a/.gitlab-ci-main.yml b/.gitlab-ci-main.yml index add5f32..face210 100644 --- a/.gitlab-ci-main.yml +++ b/.gitlab-ci-main.yml @@ -47,7 +47,7 @@ variables: # Set this to the contents of bootstrap/sha1sum.txt # which is generated by bootstrap/template.py --render # - SAMBA_CI_CONTAINER_TAG: 9a406973474a7903fe7fd6215226660911ed73c0 + SAMBA_CI_CONTAINER_TAG: b078783e082ead539940faaa644567bf4ed67f67 # # We use the ubuntu2204 image as default as # it matches what we have on atb-devel-224 @@ -112,8 +112,14 @@ include: before_script: - uname -a + - ls -l /sys/module/ + - ls -l /sys/kernel/security/ + - if [ -e /sys/kernel/security/lsm ]; then cat /sys/kernel/security/lsm ; echo; fi + - if [ -e /proc/config.gz ]; then sudo zcat /proc/config.gz; echo; fi - lsb_release -a - cat /etc/os-release + - id + - cat /proc/self/status - lscpu - cat /proc/cpuinfo - mount @@ -141,6 +147,7 @@ include: - ccache -s # We are already running .gitlab-ci directives from this repo, remove additional checks that break our CI - git config --global --add safe.directory `pwd` + - git config --global --add safe.directory /builds/samba-team/devel/samba/.git after_script: - mount - df -h @@ -27,7 +27,7 @@ SAMBA_COPYRIGHT_STRING="Copyright Andrew Tridgell and the Samba Team 1992-2024" ######################################################## SAMBA_VERSION_MAJOR=4 SAMBA_VERSION_MINOR=20 -SAMBA_VERSION_RELEASE=1 +SAMBA_VERSION_RELEASE=2 ######################################################## # If a official release has a serious bug # diff --git a/WHATSNEW.txt b/WHATSNEW.txt index 8249e93..fb964d7 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -1,4 +1,87 @@ ============================== + Release Notes for Samba 4.20.2 + June 19, 2024 + ============================== + + +This is the latest stable release of the Samba 4.20 release series. + + +Changes since 4.20.1 +-------------------- + +o Jeremy Allison <jra@samba.org> + * BUG 15662: vfs_widelinks with DFS shares breaks case insensitivity. + +o Douglas Bagnall <douglas.bagnall@catalyst.net.nz> + * BUG 13213: Samba build is not reproducible. + * BUG 15569: ldb qsort might r/w out of bounds with an intransitive compare + function. + * BUG 15625: Many qsort() comparison functions are non-transitive, which can + lead to out-of-bounds access in some circumstances. + +o Andrew Bartlett <abartlet@samba.org> + * BUG 15638: Need to change gitlab-ci.yml tags in all branches to avoid CI + bill. + * BUG 15654: We have added new options --vendor-name and --vendor-patch- + revision arguments to ./configure to allow distributions and packagers to + put their name in the Samba version string so that when debugging Samba the + source of the binary is obvious. + +o Günther Deschner <gd@samba.org> + * BUG 15665: CTDB RADOS mutex helper misses namespace support. + +o Stefan Metzmacher <metze@samba.org> + * BUG 13019: Dynamic DNS updates with the internal DNS are not working. + * BUG 14981: netr_LogonSamLogonEx returns NR_STATUS_ACCESS_DENIED with + SysvolReady=0. + * BUG 15412: Anonymous smb3 signing/encryption should be allowed (similar to + Windows Server 2022). + * BUG 15573: Panic in dreplsrv_op_pull_source_apply_changes_trigger. + * BUG 15620: s4:nbt_server: does not provide unexpected handling, so winbindd + can't use nmb requests instead cldap. + * BUG 15642: winbindd, net ads join and other things don't work on an ipv6 + only host. + * BUG 15659: Segmentation fault when deleting files in vfs_recycle. + * BUG 15664: Panic in vfs_offload_token_db_fetch_fsp(). + * BUG 15666: "client use kerberos" and --use-kerberos is ignored for the + machine account. + +o Noel Power <noel.power@suse.com> + * BUG 15435: Regression DFS not working with widelinks = true. + +o Andreas Schneider <asn@samba.org> + * BUG 15633: samba-gpupdate - Invalid NtVer in netlogon_samlogon_response. + * BUG 15653: idmap_ad creates an incorrect local krb5.conf in case of trusted + domain lookups. + * BUG 15660: The images don't build after the git security release and CentOS + 8 Stream is EOL. + + +####################################### +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.20.1 May 08, 2024 ============================== @@ -51,8 +134,7 @@ database (https://bugzilla.samba.org/). ====================================================================== -Release notes for older releases follow: ----------------------------------------- +---------------------------------------------------------------------- ============================== Release Notes for Samba 4.20.0 March 27, 2024 diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c index 20ab858..e563be3 100644 --- a/auth/credentials/credentials.c +++ b/auth/credentials/credentials.c @@ -146,6 +146,11 @@ _PUBLIC_ enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct return creds->kerberos_state; } +_PUBLIC_ enum credentials_obtained cli_credentials_get_kerberos_state_obtained(struct cli_credentials *creds) +{ + return creds->kerberos_state_obtained; +} + _PUBLIC_ const char *cli_credentials_get_forced_sasl_mech(struct cli_credentials *creds) { return creds->forced_sasl_mech; diff --git a/auth/credentials/credentials.h b/auth/credentials/credentials.h index 341c984..16eddcc 100644 --- a/auth/credentials/credentials.h +++ b/auth/credentials/credentials.h @@ -267,6 +267,7 @@ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cr const char *cli_credentials_get_self_service(struct cli_credentials *cred); const char *cli_credentials_get_target_service(struct cli_credentials *cred); enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds); +enum credentials_obtained cli_credentials_get_kerberos_state_obtained(struct cli_credentials *creds); const char *cli_credentials_get_forced_sasl_mech(struct cli_credentials *cred); enum credentials_krb_forwardable cli_credentials_get_krb_forwardable(struct cli_credentials *creds); NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred, diff --git a/auth/credentials/credentials_secrets.c b/auth/credentials/credentials_secrets.c index 8469d6e..906f3ff 100644 --- a/auth/credentials/credentials_secrets.c +++ b/auth/credentials/credentials_secrets.c @@ -370,13 +370,17 @@ _PUBLIC_ NTSTATUS cli_credentials_set_machine_account_db_ctx(struct cli_credenti } if (secrets_tdb_password_more_recent) { - enum credentials_use_kerberos use_kerberos = - CRED_USE_KERBEROS_DISABLED; char *machine_account = talloc_asprintf(tmp_ctx, "%s$", lpcfg_netbios_name(lp_ctx)); cli_credentials_set_password(cred, secrets_tdb_password, CRED_SPECIFIED); cli_credentials_set_old_password(cred, secrets_tdb_old_password, CRED_SPECIFIED); cli_credentials_set_domain(cred, domain, CRED_SPECIFIED); if (strequal(domain, lpcfg_workgroup(lp_ctx))) { + enum credentials_use_kerberos use_kerberos = + cli_credentials_get_kerberos_state(cred); + enum credentials_obtained use_kerberos_obtained = + cli_credentials_get_kerberos_state_obtained(cred); + bool is_ad = false; + cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_SPECIFIED); switch (server_role) { @@ -388,13 +392,28 @@ _PUBLIC_ NTSTATUS cli_credentials_set_machine_account_db_ctx(struct cli_credenti FALL_THROUGH; case ROLE_ACTIVE_DIRECTORY_DC: case ROLE_IPA_DC: - use_kerberos = CRED_USE_KERBEROS_DESIRED; + is_ad = true; break; } + + if (use_kerberos != CRED_USE_KERBEROS_DESIRED || is_ad) { + /* + * Keep an explicit selection + * + * For AD domains we also keep + * CRED_USE_KERBEROS_DESIRED + */ + } else if (use_kerberos_obtained <= CRED_SMB_CONF) { + /* + * Disable kerberos by default within + * an NT4 domain. + */ + cli_credentials_set_kerberos_state(cred, + CRED_USE_KERBEROS_DISABLED, + CRED_SMB_CONF); + } } - cli_credentials_set_kerberos_state(cred, - use_kerberos, - CRED_SPECIFIED); + cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED); cli_credentials_set_password_last_changed_time(cred, secrets_tdb_lct); cli_credentials_set_secure_channel_type(cred, secrets_tdb_secure_channel_type); diff --git a/auth/credentials/tests/test_creds.c b/auth/credentials/tests/test_creds.c index 2cb2e6d..e79f089 100644 --- a/auth/credentials/tests/test_creds.c +++ b/auth/credentials/tests/test_creds.c @@ -227,6 +227,8 @@ static void torture_creds_krb5_state(void **state) TALLOC_CTX *mem_ctx = *state; struct cli_credentials *creds = NULL; struct loadparm_context *lp_ctx = NULL; + enum credentials_obtained kerberos_state_obtained; + enum credentials_use_kerberos kerberos_state; bool ok; lp_ctx = loadparm_init_global(true); @@ -234,18 +236,27 @@ static void torture_creds_krb5_state(void **state) creds = cli_credentials_init(mem_ctx); assert_non_null(creds); - assert_int_equal(creds->kerberos_state_obtained, CRED_UNINITIALISED); - assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_DESIRED); + kerberos_state_obtained = + cli_credentials_get_kerberos_state_obtained(creds); + kerberos_state = cli_credentials_get_kerberos_state(creds); + assert_int_equal(kerberos_state_obtained, CRED_UNINITIALISED); + assert_int_equal(kerberos_state, CRED_USE_KERBEROS_DESIRED); ok = cli_credentials_set_conf(creds, lp_ctx); assert_true(ok); - assert_int_equal(creds->kerberos_state_obtained, CRED_SMB_CONF); - assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_DESIRED); + kerberos_state_obtained = + cli_credentials_get_kerberos_state_obtained(creds); + kerberos_state = cli_credentials_get_kerberos_state(creds); + assert_int_equal(kerberos_state_obtained, CRED_SMB_CONF); + assert_int_equal(kerberos_state, CRED_USE_KERBEROS_DESIRED); ok = cli_credentials_guess(creds, lp_ctx); assert_true(ok); - assert_int_equal(creds->kerberos_state_obtained, CRED_SMB_CONF); - assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_DESIRED); + kerberos_state_obtained = + cli_credentials_get_kerberos_state_obtained(creds); + kerberos_state = cli_credentials_get_kerberos_state(creds); + assert_int_equal(kerberos_state_obtained, CRED_SMB_CONF); + assert_int_equal(kerberos_state, CRED_USE_KERBEROS_DESIRED); assert_int_equal(creds->ccache_obtained, CRED_GUESS_FILE); assert_non_null(creds->ccache); @@ -253,15 +264,21 @@ static void torture_creds_krb5_state(void **state) CRED_USE_KERBEROS_REQUIRED, CRED_SPECIFIED); assert_true(ok); - assert_int_equal(creds->kerberos_state_obtained, CRED_SPECIFIED); - assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_REQUIRED); + kerberos_state_obtained = + cli_credentials_get_kerberos_state_obtained(creds); + kerberos_state = cli_credentials_get_kerberos_state(creds); + assert_int_equal(kerberos_state_obtained, CRED_SPECIFIED); + assert_int_equal(kerberos_state, CRED_USE_KERBEROS_REQUIRED); ok = cli_credentials_set_kerberos_state(creds, CRED_USE_KERBEROS_DISABLED, CRED_SMB_CONF); assert_false(ok); - assert_int_equal(creds->kerberos_state_obtained, CRED_SPECIFIED); - assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_REQUIRED); + kerberos_state_obtained = + cli_credentials_get_kerberos_state_obtained(creds); + kerberos_state = cli_credentials_get_kerberos_state(creds); + assert_int_equal(kerberos_state_obtained, CRED_SPECIFIED); + assert_int_equal(kerberos_state, CRED_USE_KERBEROS_REQUIRED); } diff --git a/auth/gensec/gensec_start.c b/auth/gensec/gensec_start.c index 072188a..bcf98bd 100644 --- a/auth/gensec/gensec_start.c +++ b/auth/gensec/gensec_start.c @@ -1103,7 +1103,7 @@ _PUBLIC_ const struct gensec_critical_sizes *gensec_interface_version(void) } static int sort_gensec(const struct gensec_security_ops **gs1, const struct gensec_security_ops **gs2) { - return (*gs2)->priority - (*gs1)->priority; + return NUMERIC_CMP((*gs2)->priority, (*gs1)->priority); } int gensec_setting_int(struct gensec_settings *settings, const char *mechanism, const char *name, int default_value) diff --git a/bootstrap/.gitlab-ci.yml b/bootstrap/.gitlab-ci.yml index ba82cdc..77b4e4f 100644 --- a/bootstrap/.gitlab-ci.yml +++ b/bootstrap/.gitlab-ci.yml @@ -6,9 +6,7 @@ # We need to make sure we only use gitlab.com # runners and not our own runners, as our current runners # don't allow 'docker build ...' to run. - - docker - - gce - - shared + - saas-linux-small-amd64 variables: SAMBA_CI_IS_BROKEN_IMAGE: "no" SAMBA_CI_TEST_JOB: "samba-o3" @@ -47,7 +45,7 @@ diff -u bootstrap/sha1sum.txt /tmp/sha1sum-template.txt # run smoke test with samba-o3 or samba-fuzz podman run --volume $(pwd):/src:ro ${ci_image_name} \ - /bin/bash -c "git clone /src samba && cd samba && export PKG_CONFIG_PATH=/usr/lib64/compat-gnutls34/pkgconfig:/usr/lib64/compat-nettle32/pkgconfig && script/autobuild.py ${SAMBA_CI_TEST_JOB} --verbose --nocleanup --keeplogs --tail --testbase /tmp/samba-testbase" + /bin/bash -c "git config --global --add safe.directory /src/.git && git clone /src samba && cd samba && export PKG_CONFIG_PATH=/usr/lib64/compat-gnutls34/pkgconfig:/usr/lib64/compat-nettle32/pkgconfig && script/autobuild.py ${SAMBA_CI_TEST_JOB} --verbose --nocleanup --keeplogs --tail --testbase /tmp/samba-testbase" podman tag ${ci_image_name} ${ci_image_path}:${SAMBA_CI_CONTAINER_TAG} podman tag ${ci_image_name} ${ci_image_path}:${timestamp_tag} # We build all images, but only upload is it's not marked as broken diff --git a/bootstrap/config.py b/bootstrap/config.py index 11d8314..a5a7366 100644 --- a/bootstrap/config.py +++ b/bootstrap/config.py @@ -241,6 +241,9 @@ CENTOS8S_YUM_BOOTSTRAP = r""" {GENERATED_MARKER} set -xueo pipefail +sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* +sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* + yum update -y yum install -y dnf-plugins-core yum install -y epel-release diff --git a/bootstrap/generated-dists/centos8s/bootstrap.sh b/bootstrap/generated-dists/centos8s/bootstrap.sh index 4b2c62c..9e0aabb 100755 --- a/bootstrap/generated-dists/centos8s/bootstrap.sh +++ b/bootstrap/generated-dists/centos8s/bootstrap.sh @@ -7,6 +7,9 @@ set -xueo pipefail +sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* +sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* + yum update -y yum install -y dnf-plugins-core yum install -y epel-release diff --git a/bootstrap/sha1sum.txt b/bootstrap/sha1sum.txt index 61ecaf0..1bb5e92 100644 --- a/bootstrap/sha1sum.txt +++ b/bootstrap/sha1sum.txt @@ -1 +1 @@ -9a406973474a7903fe7fd6215226660911ed73c0 +b078783e082ead539940faaa644567bf4ed67f67 diff --git a/buildtools/wafsamba/samba_pidl.py b/buildtools/wafsamba/samba_pidl.py index 72997c8..e101086 100644 --- a/buildtools/wafsamba/samba_pidl.py +++ b/buildtools/wafsamba/samba_pidl.py @@ -81,7 +81,9 @@ def SAMBA_PIDL(bld, pname, source, else: cc = 'CC="%s"' % bld.CONFIG_GET("CC") - t = bld(rule='cd ${PIDL_LAUNCH_DIR} && %s%s %s ${PERL} ${PIDL} --quiet ${OPTIONS} --outputdir ${OUTPUTDIR} -- "${IDLSRC}"' % (pidl_dev, cpp, cc), + t = bld(rule=('cd ${PIDL_LAUNCH_DIR} && PERL_HASH_SEED=0 %s%s %s ${PERL} ' + '${PIDL} --quiet ${OPTIONS} --outputdir ${OUTPUTDIR} -- "${IDLSRC}"' % + (pidl_dev, cpp, cc)), ext_out = '.c', before = 'c', update_outputs = True, diff --git a/buildtools/wafsamba/samba_third_party.py b/buildtools/wafsamba/samba_third_party.py index 5289848..a42bb2d 100644 --- a/buildtools/wafsamba/samba_third_party.py +++ b/buildtools/wafsamba/samba_third_party.py @@ -24,7 +24,7 @@ Build.BuildContext.CHECK_CMOCKA = CHECK_CMOCKA @conf def CHECK_SOCKET_WRAPPER(conf): - return conf.CHECK_BUNDLED_SYSTEM_PKG('socket_wrapper', minversion='1.4.2') + return conf.CHECK_BUNDLED_SYSTEM_PKG('socket_wrapper', minversion='1.4.3') Build.BuildContext.CHECK_SOCKET_WRAPPER = CHECK_SOCKET_WRAPPER @conf @@ -39,7 +39,7 @@ Build.BuildContext.CHECK_RESOLV_WRAPPER = CHECK_RESOLV_WRAPPER @conf def CHECK_UID_WRAPPER(conf): - return conf.CHECK_BUNDLED_SYSTEM_PKG('uid_wrapper', minversion='1.3.0') + return conf.CHECK_BUNDLED_SYSTEM_PKG('uid_wrapper', minversion='1.3.1') Build.BuildContext.CHECK_UID_WRAPPER = CHECK_UID_WRAPPER @conf diff --git a/buildtools/wafsamba/samba_version.py b/buildtools/wafsamba/samba_version.py index 31103e0..576168f 100644 --- a/buildtools/wafsamba/samba_version.py +++ b/buildtools/wafsamba/samba_version.py @@ -253,6 +253,11 @@ def samba_version_file(version_file, path, env=None, is_install=True): print("Failed to parse line %s from %s" % (line, version_file)) raise + if "SAMBA_VERSION_VENDOR_SUFFIX" in env: + version_dict["SAMBA_VERSION_VENDOR_SUFFIX"] = env.SAMBA_VERSION_VENDOR_SUFFIX + if "SAMBA_VERSION_VENDOR_PATCH" in env: + version_dict["SAMBA_VERSION_VENDOR_PATCH"] = str(env.SAMBA_VERSION_VENDOR_PATCH) + return SambaVersion(version_dict, path, env=env, is_install=is_install) diff --git a/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.xml b/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.xml index f558f87..93d79ce 100644 --- a/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.xml +++ b/ctdb/doc/ctdb_mutex_ceph_rados_helper.7.xml @@ -29,12 +29,14 @@ <manvolnum>5</manvolnum></citerefentry>: </para> <screen format="linespecific"> -cluster lock = !ctdb_mutex_ceph_rados_helper [Cluster] [User] [Pool] [Object] +cluster lock = !ctdb_mutex_ceph_rados_helper [Cluster] [User] [Pool] [Object] [Timeout] [-n Namespace] Cluster: Ceph cluster name (e.g. ceph) User: Ceph cluster user name (e.g. client.admin) Pool: Ceph RADOS pool name Object: Ceph RADOS object name +Timeout: Ceph RADOS lock duration in seconds (optional) +Namespace: Ceph RADOS pool namespace (optional) </screen> <para> The Ceph cluster <parameter>Cluster</parameter> must be up and running, diff --git a/ctdb/utils/ceph/ctdb_mutex_ceph_rados_helper.c b/ctdb/utils/ceph/ctdb_mutex_ceph_rados_helper.c index 7d868a3..46566c9 100644 --- a/ctdb/utils/ceph/ctdb_mutex_ceph_rados_helper.c +++ b/ctdb/utils/ceph/ctdb_mutex_ceph_rados_helper.c @@ -42,9 +42,18 @@ static char *progname = NULL; +static void usage(void) +{ + fprintf(stderr, "Usage: %s <Ceph Cluster> <Ceph user> " + "<RADOS pool> <RADOS object> " + "[lock duration secs] [-n RADOS namespace]\n", + progname); +} + static int ctdb_mutex_rados_ctx_create(const char *ceph_cluster_name, const char *ceph_auth_name, const char *pool_name, + const char *namespace, rados_t *_ceph_cluster, rados_ioctx_t *_ioctx) { @@ -87,6 +96,10 @@ static int ctdb_mutex_rados_ctx_create(const char *ceph_cluster_name, return ret; } + if (namespace != NULL) { + rados_ioctx_set_namespace(ioctx, namespace); + } + *_ceph_cluster = ceph_cluster; *_ioctx = ioctx; @@ -145,6 +158,7 @@ struct ctdb_mutex_rados_state { const char *ceph_cluster_name; const char *ceph_auth_name; const char *pool_name; + const char *namespace; const char *object; uint64_t lock_duration_s; int ppid; @@ -295,15 +309,13 @@ static int ctdb_mutex_rados_mgr_reg(rados_t ceph_cluster) int main(int argc, char *argv[]) { int ret; + int opt; struct ctdb_mutex_rados_state *cmr_state; progname = argv[0]; - if ((argc != 5) && (argc != 6)) { - fprintf(stderr, "Usage: %s <Ceph Cluster> <Ceph user> " - "<RADOS pool> <RADOS object> " - "[lock duration secs]\n", - progname); + if (argc < 5) { + usage(); ret = -EINVAL; goto err_out; } @@ -325,15 +337,36 @@ int main(int argc, char *argv[]) cmr_state->ceph_auth_name = argv[2]; cmr_state->pool_name = argv[3]; cmr_state->object = argv[4]; - if (argc == 6) { + + optind = 5; + while ((opt = getopt(argc, argv, "n:")) != -1) { + switch(opt) { + case 'n': + cmr_state->namespace = optarg; + break; + default: + usage(); + ret = -EINVAL; + goto err_ctx_cleanup; + } + } + + if (argv[optind] != NULL) { /* optional lock duration provided */ char *endptr = NULL; - cmr_state->lock_duration_s = strtoull(argv[5], &endptr, 0); - if ((endptr == argv[5]) || (*endptr != '\0')) { + cmr_state->lock_duration_s = strtoull(argv[optind], &endptr, 0); + if ((endptr == argv[optind]) || (*endptr != '\0')) { fprintf(stdout, CTDB_MUTEX_STATUS_ERROR); ret = -EINVAL; goto err_ctx_cleanup; } + if (argv[++optind] != NULL) { + /* incorrect count or format for optional arguments */ + usage(); + ret = -EINVAL; + goto err_ctx_cleanup; + } + } else { cmr_state->lock_duration_s = CTDB_MUTEX_CEPH_LOCK_DURATION_SECS_DEFAULT; @@ -398,6 +431,7 @@ int main(int argc, char *argv[]) ret = ctdb_mutex_rados_ctx_create(cmr_state->ceph_cluster_name, cmr_state->ceph_auth_name, cmr_state->pool_name, + cmr_state->namespace, &cmr_state->ceph_cluster, &cmr_state->ioctx); if (ret < 0) { diff --git a/examples/winexe/wscript b/examples/winexe/wscript index 6b311b1..c4f13b8 100644 --- a/examples/winexe/wscript +++ b/examples/winexe/wscript @@ -1,4 +1,6 @@ #!/usr/bin/env python +import os + def configure(conf): AR32 = ['i386', 'i586', 'i686'] @@ -27,5 +29,24 @@ def configure(conf): conf.DEFINE('HAVE_WINEXE_CC_WIN64', 1); break + source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH') + if source_date_epoch is None: + # We use the version to make up the timestamp that will be + # embedded in winexe.exe, to keep the build reproducible. + # + # This is less evil than it sounds. According to Raymond Chen in + # https://devblogs.microsoft.com/oldnewthing/20180103-00/?p=97705 + # since Windows 10 the timestamp has been randomised. + # + # The purpose of the timestamp in Windows PE files seems to be + # to make spotting ABI changes in DLLs quicker, for which a + # random number is just as good as a real time. The timestamp + # in .exe files is not used. + import samba_version + v = samba_version.load_version(env=conf.env) + version = (v.MAJOR << 16) | (v.MINOR << 8) | v.RELEASE + source_date_epoch = str(version) + + conf.env.SOURCE_DATE_EPOCH = source_date_epoch conf.DEFINE("WINEXE_LDFLAGS", "-s -Wall -Wl,-Bstatic -Wl,-Bdynamic -luserenv") diff --git a/examples/winexe/wscript_build b/examples/winexe/wscript_build index 3646834..1fe019c 100644 --- a/examples/winexe/wscript_build +++ b/examples/winexe/wscript_build @@ -69,7 +69,7 @@ bld.SAMBA_GENERATOR( 'winexesvc32_exe', source='winexesvc.c', target='winexesvc32.exe', - rule='${WINEXE_CC_WIN32} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}', + rule='SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} ${WINEXE_CC_WIN32} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}', enabled=bld.env.build_winexe and bld.env.WINEXE_CC_WIN32) vars = {"WINEXE_FN": "winexesvc32_exe_binary"} @@ -89,7 +89,7 @@ bld.SAMBA_GENERATOR( 'winexesvc64_exe', source='winexesvc.c', target='winexesvc64.exe', - rule='${WINEXE_CC_WIN64} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}', + rule='SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} ${WINEXE_CC_WIN64} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}', enabled=bld.env.build_winexe and bld.env.WINEXE_CC_WIN64) vars = {"WINEXE_FN": "winexesvc64_exe_binary"} diff --git a/lib/fuzzing/decode_ndr_X_crash b/lib/fuzzing/decode_ndr_X_crash index 63c3cd7..d90e7ef 100755 --- a/lib/fuzzing/decode_ndr_X_crash +++ b/lib/fuzzing/decode_ndr_X_crash @@ -61,8 +61,9 @@ def process_one_file(f): def main(): parser = argparse.ArgumentParser() - parser.add_argument('-p', '--pipe', default='$PIPE', - help='pipe name (for output command line)') + parser.add_argument('-p', '--pipe', default=None, + help=('pipe name (for output command line, ' + 'default is a guess or "$PIPE")')) parser.add_argument('-t', '--type', default=None, choices=TYPES, help='restrict to this type') parser.add_argument('-o', '--opnum', default=None, type=int, @@ -91,6 +92,13 @@ def main(): sys.exit(1) for fn in args.FILES: + if pipe is None: + m = re.search(r'clusterfuzz-testcase.+-fuzz_ndr_([a-z]+)', fn) + if m is None: + pipe = '$PIPE' + else: + pipe = m.group(1) + if args.crash_filter is not None: if not re.search(args.crash_filter, fn): print_if_verbose(f"skipping {fn}") diff --git a/lib/ldb-samba/ldif_handlers.c b/lib/ldb-samba/ldif_handlers.c index c30fd63..4f9d67e 100644 --- a/lib/ldb-samba/ldif_handlers.c +++ b/lib/ldb-samba/ldif_handlers.c @@ -150,36 +150,47 @@ bool ldif_comparision_objectSid_isString(const struct ldb_val *v) /* compare two objectSids + + If the SIDs seem to be strings, they are converted to binary form. */ static int ldif_comparison_objectSid(struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *v1, const struct ldb_val *v2) { - if (ldif_comparision_objectSid_isString(v1) && ldif_comparision_objectSid_isString(v2)) { - return ldb_comparison_binary(ldb, mem_ctx, v1, v2); - } else if (ldif_comparision_objectSid_isString(v1) - && !ldif_comparision_objectSid_isString(v2)) { - struct ldb_val v; - int ret; - if (ldif_read_objectSid(ldb, mem_ctx, v1, &v) != 0) { - /* Perhaps not a string after all */ - return ldb_comparison_binary(ldb, mem_ctx, v1, v2); + bool v1_is_string = ldif_comparision_objectSid_isString(v1); + bool v2_is_string = ldif_comparision_objectSid_isString(v2); + struct ldb_val parsed_1 = {}; + struct ldb_val parsed_2 = {}; + int ret; + /* + * If the ldb_vals look like SID strings (i.e. start with "S-" + * or "s-"), we try to parse them as such. If that fails, we + * assume they are binary SIDs, even though that's not really + * possible -- the first two bytes of a struct dom_sid are the + * version (1), and the number of sub-auths (<= 15), neither + * of which are close to 'S' or '-'. + */ + if (v1_is_string) { + int r = ldif_read_objectSid(ldb, mem_ctx, v1, &parsed_1); + if (r == 0) { + v1 = &parsed_1; } - ret = ldb_comparison_binary(ldb, mem_ctx, &v, v2); - talloc_free(v.data); - return ret; - } else if (!ldif_comparision_objectSid_isString(v1) - && ldif_comparision_objectSid_isString(v2)) { - struct ldb_val v; - int ret; - if (ldif_read_objectSid(ldb, mem_ctx, v2, &v) != 0) { - /* Perhaps not a string after all */ - return ldb_comparison_binary(ldb, mem_ctx, v1, v2); + } + if (v2_is_string) { + int r = ldif_read_objectSid(ldb, mem_ctx, v2, &parsed_2); + if (r == 0) { + v2 = &parsed_2; } - ret = ldb_comparison_binary(ldb, mem_ctx, v1, &v); - talloc_free(v.data); - return ret; } - return ldb_comparison_binary(ldb, mem_ctx, v1, v2); + + ret = ldb_comparison_binary(ldb, mem_ctx, v1, v2); + + if (v1_is_string) { + TALLOC_FREE(parsed_1.data); + } + if (v2_is_string) { + TALLOC_FREE(parsed_2.data); + } + return ret; } /* @@ -1148,22 +1159,41 @@ static int samba_ldb_dn_link_comparison(struct ldb_context *ldb, void *mem_ctx, struct ldb_dn *dn1 = NULL, *dn2 = NULL; int ret; + /* + * In a sort context, Deleted DNs get shifted to the end. + * They never match in an equality + */ if (dsdb_dn_is_deleted_val(v1)) { - /* If the DN is deleted, then we can't search for it */ - return -1; - } - - if (dsdb_dn_is_deleted_val(v2)) { - /* If the DN is deleted, then we can't search for it */ + if (! dsdb_dn_is_deleted_val(v2)) { + return 1; + } + /* + * They are both deleted! + * + * The soundest thing to do at this point is carry on + * and compare the DNs normally. This matches the + * behaviour of samba_dn_extended_match() below. + */ + } else if (dsdb_dn_is_deleted_val(v2)) { return -1; } dn1 = ldb_dn_from_ldb_val(mem_ctx, ldb, v1); - if ( ! ldb_dn_validate(dn1)) return -1; - dn2 = ldb_dn_from_ldb_val(mem_ctx, ldb, v2); + + if ( ! ldb_dn_validate(dn1)) { + TALLOC_FREE(dn1); + if ( ! ldb_dn_validate(dn2)) { + TALLOC_FREE(dn2); + return 0; + } + TALLOC_FREE(dn2); + return 1; + } + if ( ! ldb_dn_validate(dn2)) { - talloc_free(dn1); + TALLOC_FREE(dn1); + TALLOC_FREE(dn2); return -1; } diff --git a/lib/ldb/ABI/ldb-2.9.1.sigs b/lib/ldb/ABI/ldb-2.9.1.sigs new file mode 100644 index 0000000..759659a --- /dev/null +++ b/lib/ldb/ABI/ldb-2.9.1.sigs @@ -0,0 +1,305 @@ +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_attrs_in_place: int (struct ldb_message *, const char * const *) +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_match_scope: int (struct ldb_context *, struct ldb_dn *, struct ldb_dn *, enum ldb_scope) +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_distinguished_name: int (struct ldb_message *) +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_string_flags: int (struct ldb_message *, const char *, const char *, int) +ldb_msg_add_value: int (struct ldb_message *, const char *, const struct ldb_val *, struct ldb_message_element **) +ldb_msg_append_fmt: int (struct ldb_message *, int, const char *, const char *, ...) +ldb_msg_append_linearized_dn: int (struct ldb_message *, const char *, struct ldb_dn *, int) +ldb_msg_append_steal_string: int (struct ldb_message *, const char *, char *, int) +ldb_msg_append_steal_value: int (struct ldb_message *, const char *, struct ldb_val *, int) +ldb_msg_append_string: int (struct ldb_message *, const char *, const char *, int) +ldb_msg_append_value: int (struct ldb_message *, const char *, const struct ldb_val *, int) +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_add_value: int (TALLOC_CTX *, struct ldb_message_element *, const struct ldb_val *) +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_element_is_inaccessible: bool (const struct ldb_message_element *) +ldb_msg_element_mark_inaccessible: void (struct ldb_message_element *) +ldb_msg_elements_take_ownership: int (struct ldb_message *) +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_remove_inaccessible: void (struct ldb_message *) +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_shrink_to_fit: void (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_get_attr: const char *(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_register_redact_callback: int (struct ldb_context *, ldb_redact_fn, struct ldb_module *) +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_as_bool: int (const struct ldb_val *, bool *) +ldb_val_as_dn: struct ldb_dn *(struct ldb_context *, TALLOC_CTX *, const struct ldb_val *) +ldb_val_as_int64: int (const struct ldb_val *, int64_t *) +ldb_val_as_uint64: int (const struct ldb_val *, uint64_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.9.1.sigs b/lib/ldb/ABI/pyldb-util-2.9.1.sigs new file mode 100644 index 0000000..164a806 --- /dev/null +++ b/lib/ldb/ABI/pyldb-util-2.9.1.sigs @@ -0,0 +1,3 @@ +pyldb_Dn_FromDn: PyObject *(struct ldb_dn *) +pyldb_Object_AsDn: bool (TALLOC_CTX *, PyObject *, struct ldb_context *, struct ldb_dn **) +pyldb_check_type: bool (PyObject *, const char *) diff --git a/lib/ldb/common/attrib_handlers.c b/lib/ldb/common/attrib_handlers.c index 15470cf..3d13e4b 100644 --- a/lib/ldb/common/attrib_handlers.c +++ b/lib/ldb/common/attrib_handlers.c @@ -281,15 +281,36 @@ static int ldb_canonicalise_Boolean(struct ldb_context *ldb, void *mem_ctx, } /* - compare two Booleans -*/ + * compare two Booleans. + * + * According to RFC4517 4.2.2, "the booleanMatch rule is an equality matching + * rule", meaning it isn't used for ordering. + * + * However, it seems conceivable that Samba could be coerced into sorting on a + * field with Boolean syntax, so we might as well have consistent behaviour in + * that case. + * + * The most probably values are {"FALSE", 5} and {"TRUE", 4}. To save time we + * compare first by length, which makes FALSE > TRUE. This is somewhat + * contrary to convention, but is how Samba has worked forever. + * + * If somehow we are comparing incompletely normalised values where the length + * is the same (for example {"false", 5} and {"TRUE\0", 5}), the length is the + * same, and we fall back to a strncasecmp. In this case, since "FALSE" is + * alphabetically lower, we swap the order, so that "TRUE\0" again comes + * before "FALSE". + * + * ldb_canonicalise_Boolean (just above) gives us a clue as to what we might + * expect to cope with by way of invalid values. + */ static int ldb_comparison_Boolean(struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *v1, const struct ldb_val *v2) { if (v1->length != v2->length) { - return v1->length - v2->length; + return NUMERIC_CMP(v2->length, v1->length); } - return strncasecmp((char *)v1->data, (char *)v2->data, v1->length); + /* reversed, see long comment above */ + return strncasecmp((char *)v2->data, (char *)v1->data, v1->length); } @@ -300,7 +321,7 @@ int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *v1, const struct ldb_val *v2) { if (v1->length != v2->length) { - return v1->length - v2->length; + return NUMERIC_CMP(v1->length, v2->length); } return memcmp(v1->data, v2->data, v1->length); } @@ -372,17 +393,27 @@ utf8str: b2 = ldb_casefold(ldb, mem_ctx, s2, n2); if (!b1 || !b2) { - /* One of the strings was not UTF8, so we have no - * options but to do a binary compare */ + /* + * One of the strings was not UTF8, so we have no + * options but to do a binary compare. + */ talloc_free(b1); talloc_free(b2); ret = memcmp(s1, s2, MIN(n1, n2)); if (ret == 0) { - if (n1 == n2) return 0; + if (n1 == n2) { + return 0; + } if (n1 > n2) { - return (int)ldb_ascii_toupper(s1[n2]); + if (s1[n2] == '\0') { + return 0; + } + return 1; } else { - return -(int)ldb_ascii_toupper(s2[n1]); + if (s2[n1] == '\0') { + return 0; + } + return -1; } } return ret; @@ -404,7 +435,7 @@ utf8str: while (*u1 == ' ') u1++; while (*u2 == ' ') u2++; } - ret = (int)(*u1 - *u2); + ret = NUMERIC_CMP(*u1, *u2); talloc_free(b1); talloc_free(b2); diff --git a/lib/ldb/common/ldb_dn.c b/lib/ldb/common/ldb_dn.c index 601da57..8388fdb 100644 --- a/lib/ldb/common/ldb_dn.c +++ b/lib/ldb/common/ldb_dn.c @@ -1111,7 +1111,7 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn) /* compare attr.cf_value. */ if (b_vlen != dn_vlen) { - return b_vlen - dn_vlen; + return NUMERIC_CMP(b_vlen, dn_vlen); } ret = strncmp(b_vdata, dn_vdata, b_vlen); if (ret != 0) return ret; @@ -1132,8 +1132,32 @@ int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1) { unsigned int i; int ret; + /* + * If used in sort, we shift NULL and invalid DNs to the end. + * + * If ldb_dn_casefold_internal() fails, that goes to the end too, so + * we end up with: + * + * | normal DNs, sorted | casefold failed DNs | invalid DNs | NULLs | + */ - if (( ! dn0) || dn0->invalid || ! dn1 || dn1->invalid) { + if (dn0 == dn1) { + /* this includes the both-NULL case */ + return 0; + } + if (dn0 == NULL) { + return 1; + } + if (dn1 == NULL) { + return -1; + } + if (dn0->invalid && dn1->invalid) { + return 0; + } + if (dn0->invalid) { + return 1; + } + if (dn1->invalid) { return -1; } @@ -1190,7 +1214,7 @@ int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1) /* compare attr.cf_value. */ if (dn0_vlen != dn1_vlen) { - return dn0_vlen - dn1_vlen; + return NUMERIC_CMP(dn0_vlen, dn1_vlen); } ret = strncmp(dn0_vdata, dn1_vdata, dn0_vlen); if (ret != 0) { diff --git a/lib/ldb/common/ldb_msg.c b/lib/ldb/common/ldb_msg.c index afddbe4..c334d70 100644 --- a/lib/ldb/common/ldb_msg.c +++ b/lib/ldb/common/ldb_msg.c @@ -93,7 +93,7 @@ struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el, static int ldb_val_cmp(const struct ldb_val *v1, const struct ldb_val *v2) { if (v1->length != v2->length) { - return v1->length - v2->length; + return NUMERIC_CMP(v1->length, v2->length); } return memcmp(v1->data, v2->data, v1->length); } @@ -749,9 +749,16 @@ int ldb_msg_element_compare(struct ldb_message_element *el1, unsigned int i; if (el1->num_values != el2->num_values) { - return el1->num_values - el2->num_values; + return NUMERIC_CMP(el1->num_values, el2->num_values); } - + /* + * Note this is an inconsistent comparison, unsuitable for + * sorting. If A has values {a, b} and B has values {b, c}, + * then + * + * ldb_msg_element_compare(A, B) returns -1, meaning A < B + * ldb_msg_element_compare(B, A) returns -1, meaning B < A + */ for (i=0;i<el1->num_values;i++) { if (!ldb_msg_find_val(el2, &el1->values[i])) { return -1; diff --git a/lib/ldb/common/qsort.c b/lib/ldb/common/qsort.c index 012aaf3..bae35e6 100644 --- a/lib/ldb/common/qsort.c +++ b/lib/ldb/common/qsort.c @@ -227,7 +227,7 @@ void ldb_qsort (void *const pbase, size_t total_elems, size_t size, while ((run_ptr += size) <= end_ptr) { tmp_ptr = run_ptr - size; - while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0) + while (tmp_ptr > base_ptr && (*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0) tmp_ptr -= size; tmp_ptr += size; diff --git a/lib/ldb/include/ldb.h b/lib/ldb/include/ldb.h index 5d83a27..98859d4 100644 --- a/lib/ldb/include/ldb.h +++ b/lib/ldb/include/ldb.h @@ -2326,6 +2326,22 @@ do { \ } while (0) #endif +#ifndef NUMERIC_CMP +/* + * NUMERIC_CMP is a safe replacement for `a - b` in comparison + * functions. It will work on integers, pointers, and floats. + * + * Rather than + * + * return a - b; + * + * use + * + * return NUMERIC_CMP(a, b); + */ +#define NUMERIC_CMP(a, b) (((a) > (b)) - ((a) < (b))) +#endif + /** diff --git a/lib/ldb/modules/sort.c b/lib/ldb/modules/sort.c index cb6f8df..72c60fc 100644 --- a/lib/ldb/modules/sort.c +++ b/lib/ldb/modules/sort.c @@ -121,15 +121,28 @@ static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, vo el1 = ldb_msg_find_element(*msg1, ac->attributeName); el2 = ldb_msg_find_element(*msg2, ac->attributeName); - if (!el1 && el2) { + /* + * NULL and empty elements sort at the end (regardless of ac->reverse flag). + * NULL elements come after empty ones. + */ + if (el1 == el2) { + return 0; + } + if (el1 == NULL) { return 1; } - if (el1 && !el2) { + if (el2 == NULL) { return -1; } - if (!el1 && !el2) { + if (unlikely(el1->num_values == 0 && el2->num_values == 0)) { return 0; } + if (unlikely(el1->num_values == 0)) { + return 1; + } + if (unlikely(el2->num_values == 0)) { + return -1; + } if (ac->reverse) return ac->a->syntax->comparison_fn(ldb, ac, &el2->values[0], &el1->values[0]); diff --git a/lib/ldb/wscript b/lib/ldb/wscript index c249a82..936abe7 100644 --- a/lib/ldb/wscript +++ b/lib/ldb/wscript @@ -2,7 +2,7 @@ APPNAME = 'ldb' # For Samba 4.20.x ! -VERSION = '2.9.0' +VERSION = '2.9.1' import sys, os diff --git a/lib/socket/interfaces.c b/lib/socket/interfaces.c index 4908b0f..2426ce2 100644 --- a/lib/socket/interfaces.c +++ b/lib/socket/interfaces.c @@ -386,18 +386,18 @@ static int iface_comp(struct iface_struct *i1, struct iface_struct *i2) if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) { struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip; struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip; - - r = ntohl(s1->sin_addr.s_addr) - - ntohl(s2->sin_addr.s_addr); - if (r) { - return r; + uint32_t a1 = ntohl(s1->sin_addr.s_addr); + uint32_t a2 = ntohl(s2->sin_addr.s_addr); + r = NUMERIC_CMP(a1, a2); + if (r == 0) { + /* compare netmasks as a tiebreaker */ + s1 = (struct sockaddr_in *)&i1->netmask; + s2 = (struct sockaddr_in *)&i2->netmask; + a1 = ntohl(s1->sin_addr.s_addr); + a2 = ntohl(s2->sin_addr.s_addr); + r = NUMERIC_CMP(a1, a2); } - - s1 = (struct sockaddr_in *)&i1->netmask; - s2 = (struct sockaddr_in *)&i2->netmask; - - return ntohl(s1->sin_addr.s_addr) - - ntohl(s2->sin_addr.s_addr); + return r; } return 0; } diff --git a/lib/torture/torture.h b/lib/torture/torture.h index 2e86e31..2194703 100644 --- a/lib/torture/torture.h +++ b/lib/torture/torture.h @@ -534,6 +534,26 @@ static inline void torture_dump_data_str_cb(const char *buf, void *private_data) } \ } while(0) +#define torture_assert_int_less(torture_ctx,got,limit,cmt)\ + do { int __got = (got), __limit = (limit); \ + if (__got >= __limit) { \ + torture_result(torture_ctx, TORTURE_FAIL, \ + __location__": "#got" was %d (0x%X), expected < %d (0x%X): %s", \ + __got, __got, __limit, __limit, cmt); \ + return false; \ + } \ + } while(0) + +#define torture_assert_int_greater(torture_ctx,got,limit,cmt)\ + do { int __got = (got), __limit = (limit); \ + if (__got <= __limit) { \ + torture_result(torture_ctx, TORTURE_FAIL, \ + __location__": "#got" was %d (0x%X), expected > %d (0x%X): %s", \ + __got, __got, __limit, __limit, cmt); \ + return false; \ + } \ + } while(0) + #define torture_assert_int_equal_goto(torture_ctx,got,expected,ret,label,cmt)\ do { int __got = (got), __expected = (expected); \ if (__got != __expected) { \ diff --git a/lib/util/charset/codepoints.c b/lib/util/charset/codepoints.c index ea2c4be..8022627 100644 --- a/lib/util/charset/codepoints.c +++ b/lib/util/charset/codepoints.c @@ -26,6 +26,7 @@ #include "dynconfig/dynconfig.h" #include "lib/util/debug.h" #include "lib/util/byteorder.h" +#include "lib/util/tsort.h" #ifdef strcasecmp #undef strcasecmp @@ -16479,11 +16480,23 @@ _PUBLIC_ bool isupper_m(codepoint_t val) */ _PUBLIC_ int codepoint_cmpi(codepoint_t c1, codepoint_t c2) { + /* + * FIXME: this is unsuitable for use in a sort, as the + * comparison is intransitive. + * + * The problem is toupper_m() is only called on equality case, + * which has strange effects. + * + * Consider {'a', 'A', 'B'}. + * 'a' == 'A' + * 'a' > 'B' (lowercase letters come after upper) + * 'A' < 'B' + */ if (c1 == c2 || toupper_m(c1) == toupper_m(c2)) { return 0; } - return c1 - c2; + return NUMERIC_CMP(c1, c2); } diff --git a/lib/util/charset/tests/charset.c b/lib/util/charset/tests/charset.c index 547dc51..bca5449 100644 --- a/lib/util/charset/tests/charset.c +++ b/lib/util/charset/tests/charset.c @@ -72,16 +72,19 @@ static bool test_strcasecmp_m(struct torture_context *tctx) const char file_iso8859_1[7] = { 0x66, 0x69, 0x6c, 0x65, 0x2d, 0xe9, 0 }; /* file.{accented e} in utf8 */ const char file_utf8[8] = { 0x66, 0x69, 0x6c, 0x65, 0x2d, 0xc3, 0xa9, 0 }; - torture_assert_int_equal(tctx, strcasecmp_m("foo", "bar"), 4, "different strings both lower"); - torture_assert_int_equal(tctx, strcasecmp_m("foo", "Bar"), 4, "different strings lower/upper"); - torture_assert_int_equal(tctx, strcasecmp_m("Foo", "bar"), 4, "different strings upper/lower"); - torture_assert_int_equal(tctx, strcasecmp_m("AFoo", "_bar"), 2, "different strings upper/lower"); + torture_assert_int_greater(tctx, strcasecmp_m("foo", "bar"), 0, "different strings both lower"); + torture_assert_int_less(tctx, strcasecmp_m("bar", "foo"), 0, "different strings both lower"); + torture_assert_int_greater(tctx, strcasecmp_m("foo", "Bar"), 0, "different strings lower/upper"); + torture_assert_int_greater(tctx, strcasecmp_m("Foo", "bar"), 0, "different strings upper/lower"); + torture_assert_int_greater(tctx, strcasecmp_m("AFoo", "_bar"), 0, "different strings upper/lower"); torture_assert_int_equal(tctx, strcasecmp_m("foo", "foo"), 0, "same case strings"); torture_assert_int_equal(tctx, strcasecmp_m("foo", "Foo"), 0, "different case strings"); - torture_assert_int_equal(tctx, strcasecmp_m(NULL, "Foo"), -1, "one NULL"); - torture_assert_int_equal(tctx, strcasecmp_m("foo", NULL), 1, "other NULL"); + torture_assert_int_greater(tctx, strcasecmp_m("food", "Foo"), 0, "strings differ towards the end"); + torture_assert_int_less(tctx, strcasecmp_m("food", "Fool"), 0, "strings differ towards the end"); + torture_assert_int_less(tctx, strcasecmp_m(NULL, "Foo"), 0, "one NULL"); + torture_assert_int_greater(tctx, strcasecmp_m("foo", NULL), 0, "other NULL"); torture_assert_int_equal(tctx, strcasecmp_m(NULL, NULL), 0, "both NULL"); - torture_assert_int_equal(tctx, strcasecmp_m(file_iso8859_1, file_utf8), 38, + torture_assert_int_greater(tctx, strcasecmp_m(file_iso8859_1, file_utf8), 0, "file.{accented e} should differ"); return true; } @@ -151,19 +154,19 @@ static bool test_strncasecmp_m(struct torture_context *tctx) const char file_iso8859_1[7] = { 0x66, 0x69, 0x6c, 0x65, 0x2d, 0xe9, 0 }; /* file.{accented e} in utf8 */ const char file_utf8[8] = { 0x66, 0x69, 0x6c, 0x65, 0x2d, 0xc3, 0xa9, 0 }; - torture_assert_int_equal(tctx, strncasecmp_m("foo", "bar", 3), 4, "different strings both lower"); - torture_assert_int_equal(tctx, strncasecmp_m("foo", "Bar", 3), 4, "different strings lower/upper"); - torture_assert_int_equal(tctx, strncasecmp_m("Foo", "bar", 3), 4, "different strings upper/lower"); - torture_assert_int_equal(tctx, strncasecmp_m("AFoo", "_bar", 4), 2, "different strings upper/lower"); + torture_assert_int_greater(tctx, strncasecmp_m("foo", "bar", 3), 0, "different strings both lower"); + torture_assert_int_greater(tctx, strncasecmp_m("foo", "Bar", 3), 0, "different strings lower/upper"); + torture_assert_int_greater(tctx, strncasecmp_m("Foo", "bar", 3), 0, "different strings upper/lower"); + torture_assert_int_greater(tctx, strncasecmp_m("AFoo", "_bar", 4), 0, "different strings upper/lower"); torture_assert_int_equal(tctx, strncasecmp_m("foo", "foo", 3), 0, "same case strings"); torture_assert_int_equal(tctx, strncasecmp_m("foo", "Foo", 3), 0, "different case strings"); torture_assert_int_equal(tctx, strncasecmp_m("fool", "Foo", 3),0, "different case strings"); torture_assert_int_equal(tctx, strncasecmp_m("fool", "Fool", 40), 0, "over size"); torture_assert_int_equal(tctx, strncasecmp_m("BLA", "Fool", 0),0, "empty"); - torture_assert_int_equal(tctx, strncasecmp_m(NULL, "Foo", 3), -1, "one NULL"); - torture_assert_int_equal(tctx, strncasecmp_m("foo", NULL, 3), 1, "other NULL"); + torture_assert_int_less(tctx, strncasecmp_m(NULL, "Foo", 3), 0, "one NULL"); + torture_assert_int_greater(tctx, strncasecmp_m("foo", NULL, 3), 0, "other NULL"); torture_assert_int_equal(tctx, strncasecmp_m(NULL, NULL, 3), 0, "both NULL"); - torture_assert_int_equal(tctx, strncasecmp_m(file_iso8859_1, file_utf8, 6), 38, + torture_assert_int_greater(tctx, strncasecmp_m(file_iso8859_1, file_utf8, 6), 0, "file.{accented e} should differ"); return true; } diff --git a/lib/util/charset/util_str.c b/lib/util/charset/util_str.c index 1650c9b..c52b773 100644 --- a/lib/util/charset/util_str.c +++ b/lib/util/charset/util_str.c @@ -26,6 +26,7 @@ #include "system/locale.h" #include "charset.h" #include "lib/util/fault.h" +#include "lib/util/tsort.h" #ifdef strcasecmp #undef strcasecmp @@ -79,10 +80,10 @@ _PUBLIC_ int strcasecmp_m_handle(struct smb_iconv_handle *iconv_handle, continue; } - return l1 - l2; + return NUMERIC_CMP(l1, l2); } - return *s1 - *s2; + return NUMERIC_CMP(*s1, *s2); } /** @@ -156,14 +157,14 @@ _PUBLIC_ int strncasecmp_m_handle(struct smb_iconv_handle *iconv_handle, continue; } - return l1 - l2; + return NUMERIC_CMP(l1, l2); } if (n == 0) { return 0; } - return *s1 - *s2; + return NUMERIC_CMP(*s1, *s2); } /** diff --git a/lib/util/data_blob.c b/lib/util/data_blob.c index 69a340c..1558200 100644 --- a/lib/util/data_blob.c +++ b/lib/util/data_blob.c @@ -22,6 +22,7 @@ #include "attr.h" #include "data_blob.h" #include "lib/util/samba_util.h" +#include "lib/util/tsort.h" const DATA_BLOB data_blob_null = { NULL, 0 }; @@ -121,12 +122,12 @@ _PUBLIC_ int data_blob_cmp(const DATA_BLOB *d1, const DATA_BLOB *d2) return 1; } if (d1->data == d2->data) { - return d1->length - d2->length; + return NUMERIC_CMP(d1->length, d2->length); } ret = memcmp(d1->data, d2->data, MIN(d1->length, d2->length)); if (ret == 0) { /* Note this ordering is used in conditional aces */ - return d1->length - d2->length; + return NUMERIC_CMP(d1->length, d2->length); } return ret; } diff --git a/lib/util/tests/binsearch.c b/lib/util/tests/binsearch.c index b3ecda1..2484015 100644 --- a/lib/util/tests/binsearch.c +++ b/lib/util/tests/binsearch.c @@ -23,17 +23,19 @@ #include "includes.h" #include "lib/util/binsearch.h" +#include "lib/util/tsort.h" #include "torture/torture.h" #include "torture/local/proto.h" static int int_cmp(int a, int b) { - return a - b; + return NUMERIC_CMP(a, b); } static int int_cmp_p(int a, int *b) { - return a - *b; + int _b = *b; + return NUMERIC_CMP(a, _b); } static bool test_binsearch_v(struct torture_context *tctx) diff --git a/lib/util/tests/test_ms_fnmatch.c b/lib/util/tests/test_ms_fnmatch.c index d11c7be..2261f9b 100644 --- a/lib/util/tests/test_ms_fnmatch.c +++ b/lib/util/tests/test_ms_fnmatch.c @@ -36,7 +36,7 @@ static void test_ms_fn_match_protocol_no_wildcard(void **state) /* no wildcards in pattern, a simple strcasecmp_m */ cmp = ms_fnmatch_protocol("pattern", "string", PROTOCOL_COREPLUS, true); /* case sensitive */ - assert_int_equal(cmp, -3); + assert_true(cmp < 0); } static void test_ms_fn_match_protocol_pattern_upgraded(void **state) diff --git a/lib/util/tsort.h b/lib/util/tsort.h index 811d6cd..18e82d6 100644 --- a/lib/util/tsort.h +++ b/lib/util/tsort.h @@ -37,4 +37,23 @@ do { \ } while (0) #endif + +#ifndef NUMERIC_CMP +/* + * NUMERIC_CMP is a safe replacement for `a - b` in comparison + * functions. It will work on integers, pointers, and floats. + * + * Rather than + * + * return a - b; + * + * use + * + * return NUMERIC_CMP(a, b); + * + * and you won't have any troubles if a - b would overflow. + */ +#define NUMERIC_CMP(a, b) (((a) > (b)) - ((a) < (b))) +#endif + #endif diff --git a/libcli/nbt/libnbt.h b/libcli/nbt/libnbt.h index 204484b..6a30c9f 100644 --- a/libcli/nbt/libnbt.h +++ b/libcli/nbt/libnbt.h @@ -331,6 +331,9 @@ NTSTATUS nbt_set_unexpected_handler(struct nbt_name_socket *nbtsock, void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *, struct socket_address *), void *private_data); +NTSTATUS nbt_name_send_raw(struct nbt_name_socket *nbtsock, + struct socket_address *dest, + const DATA_BLOB pkt_blob); NTSTATUS nbt_name_reply_send(struct nbt_name_socket *nbtsock, struct socket_address *dest, struct nbt_name_packet *request); diff --git a/libcli/nbt/nbtsocket.c b/libcli/nbt/nbtsocket.c index 47e73cf..b2945ad 100644 --- a/libcli/nbt/nbtsocket.c +++ b/libcli/nbt/nbtsocket.c @@ -448,6 +448,50 @@ failed: return NULL; } +/* + send off a nbt name packet +*/ +_PUBLIC_ NTSTATUS nbt_name_send_raw(struct nbt_name_socket *nbtsock, + struct socket_address *dest, + const DATA_BLOB pkt_blob) +{ + struct nbt_name_request *req; + + req = talloc_zero(nbtsock, struct nbt_name_request); + NT_STATUS_HAVE_NO_MEMORY(req); + + req->nbtsock = nbtsock; + req->dest = socket_address_copy(req, dest); + if (req->dest == NULL) { + goto failed; + } + req->state = NBT_REQUEST_SEND; + /* + * We don't expect a response so + * just pretent it is a request, + * but we really don't care about the + * content. + */ + req->is_reply = true; + + req->encoded = data_blob_dup_talloc(req, pkt_blob); + if (req->encoded.length != pkt_blob.length) { + goto failed; + } + + talloc_set_destructor(req, nbt_name_request_destructor); + + DLIST_ADD_END(nbtsock->send_queue, req); + + TEVENT_FD_WRITEABLE(nbtsock->fde); + + return NT_STATUS_OK; + +failed: + talloc_free(req); + return NT_STATUS_NO_MEMORY; +} + /* send off a nbt name reply diff --git a/libcli/security/dom_sid.c b/libcli/security/dom_sid.c index eaece2a..21012b7 100644 --- a/libcli/security/dom_sid.c +++ b/libcli/security/dom_sid.c @@ -28,6 +28,7 @@ #include "librpc/gen_ndr/security.h" #include "dom_sid.h" #include "lib/util/smb_strtox.h" +#include "lib/util/tsort.h" /***************************************************************** Compare the auth portion of two sids. @@ -46,11 +47,12 @@ int dom_sid_compare_auth(const struct dom_sid *sid1, return 1; if (sid1->sid_rev_num != sid2->sid_rev_num) - return sid1->sid_rev_num - sid2->sid_rev_num; + return NUMERIC_CMP(sid1->sid_rev_num, sid2->sid_rev_num); for (i = 0; i < 6; i++) - if (sid1->id_auth[i] != sid2->id_auth[i]) - return sid1->id_auth[i] - sid2->id_auth[i]; + if (sid1->id_auth[i] != sid2->id_auth[i]) { + return NUMERIC_CMP(sid1->id_auth[i], sid2->id_auth[i]); + } return 0; } @@ -71,9 +73,9 @@ int dom_sid_compare(const struct dom_sid *sid1, const struct dom_sid *sid2) return 1; /* Compare most likely different rids, first: i.e start at end */ - if (sid1->num_auths != sid2->num_auths) - return sid1->num_auths - sid2->num_auths; - + if (sid1->num_auths != sid2->num_auths) { + return NUMERIC_CMP(sid1->num_auths, sid2->num_auths); + } for (i = sid1->num_auths-1; i >= 0; --i) { if (sid1->sub_auths[i] < sid2->sub_auths[i]) { return -1; diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index a52a615..87acddf 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -166,6 +166,13 @@ struct smb2cli_session { uint16_t channel_sequence; bool replay_active; bool require_signed_response; + + /* + * The following are just for torture tests + */ + bool anonymous_signing; + bool anonymous_encryption; + bool no_signing_disconnect; }; struct smbXcli_session { @@ -3999,6 +4006,9 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn, if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED) || NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) || + (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) && + session != NULL && + session->smb2->no_signing_disconnect) || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { /* * if the server returns @@ -4042,8 +4052,29 @@ static NTSTATUS smb2cli_conn_dispatch_incoming(struct smbXcli_conn *conn, /* * If the signing check fails, we disconnect * the connection. + * + * Unless + * smb2cli_session_torture_no_signing_disconnect + * was called in torture tests */ - return signing_status; + + if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + return signing_status; + } + + if (!NT_STATUS_EQUAL(status, signing_status)) { + return signing_status; + } + + if (session == NULL) { + return signing_status; + } + + if (!session->smb2->no_signing_disconnect) { + return signing_status; + } + + state->smb2.signing_skipped = true; } } @@ -6332,6 +6363,23 @@ void smb2cli_session_require_signed_response(struct smbXcli_session *session, session->smb2->require_signed_response = require_signed_response; } +void smb2cli_session_torture_anonymous_signing(struct smbXcli_session *session, + bool anonymous_signing) +{ + session->smb2->anonymous_signing = anonymous_signing; +} + +void smb2cli_session_torture_anonymous_encryption(struct smbXcli_session *session, + bool anonymous_encryption) +{ + session->smb2->anonymous_encryption = anonymous_encryption; +} + +void smb2cli_session_torture_no_signing_disconnect(struct smbXcli_session *session) +{ + session->smb2->no_signing_disconnect = true; +} + NTSTATUS smb2cli_session_update_preauth(struct smbXcli_session *session, const struct iovec *iov) { @@ -6432,6 +6480,10 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, conn->protocol, preauth_hash); + if (session->smb2->anonymous_encryption) { + goto skip_signing_key; + } + status = smb2_signing_key_sign_create(session->smb2, conn->smb2.server.sign_algo, &_session_key, @@ -6441,6 +6493,15 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, return status; } + if (session->smb2->anonymous_signing) { + /* + * skip encryption and application keys + */ + goto skip_application_key; + } + +skip_signing_key: + status = smb2_signing_key_cipher_create(session->smb2, conn->smb2.server.cipher, &_session_key, @@ -6459,6 +6520,10 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, return status; } + if (session->smb2->anonymous_encryption) { + goto skip_application_key; + } + status = smb2_signing_key_sign_create(session->smb2, conn->smb2.server.sign_algo, &_session_key, @@ -6468,6 +6533,8 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, return status; } +skip_application_key: + status = smb2_signing_key_copy(session, session->smb2->signing_key, &session->smb2_channel.signing_key); @@ -6477,6 +6544,18 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, check_signature = conn->mandatory_signing; + if (conn->protocol >= PROTOCOL_SMB3_11) { + check_signature = true; + } + + if (session->smb2->anonymous_signing) { + check_signature = false; + } + + if (session->smb2->anonymous_encryption) { + check_signature = false; + } + hdr_flags = IVAL(recv_iov[0].iov_base, SMB2_HDR_FLAGS); if (hdr_flags & SMB2_HDR_FLAG_SIGNED) { /* @@ -6492,10 +6571,6 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, check_signature = true; } - if (conn->protocol >= PROTOCOL_SMB3_11) { - check_signature = true; - } - if (check_signature) { status = smb2_signing_check_pdu(session->smb2_channel.signing_key, recv_iov, 3); @@ -6527,6 +6602,15 @@ NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, session->smb2->should_encrypt = false; } + if (session->smb2->anonymous_signing) { + session->smb2->should_sign = true; + } + + if (session->smb2->anonymous_encryption) { + session->smb2->should_encrypt = true; + session->smb2->should_sign = false; + } + /* * CCM and GCM algorithms must never have their * nonce wrap, or the security of the whole @@ -6698,6 +6782,16 @@ NTSTATUS smb2cli_session_set_channel_key(struct smbXcli_session *session, NTSTATUS smb2cli_session_encryption_on(struct smbXcli_session *session) { + if (session->smb2->anonymous_signing) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + + if (session->smb2->anonymous_encryption) { + SMB_ASSERT(session->smb2->should_encrypt); + SMB_ASSERT(!session->smb2->should_sign); + return NT_STATUS_OK; + } + if (!session->smb2->should_sign) { /* * We need required signing on the session diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index 25ccd84..69fa131 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -535,6 +535,11 @@ void smb2cli_session_start_replay(struct smbXcli_session *session); void smb2cli_session_stop_replay(struct smbXcli_session *session); void smb2cli_session_require_signed_response(struct smbXcli_session *session, bool require_signed_response); +void smb2cli_session_torture_anonymous_signing(struct smbXcli_session *session, + bool anonymous_signing); +void smb2cli_session_torture_anonymous_encryption(struct smbXcli_session *session, + bool anonymous_encryption); +void smb2cli_session_torture_no_signing_disconnect(struct smbXcli_session *session); NTSTATUS smb2cli_session_update_preauth(struct smbXcli_session *session, const struct iovec *iov); NTSTATUS smb2cli_session_set_session_key(struct smbXcli_session *session, diff --git a/pidl/lib/Parse/Pidl/Typelist.pm b/pidl/lib/Parse/Pidl/Typelist.pm index 31ea19e..2a98a16 100644 --- a/pidl/lib/Parse/Pidl/Typelist.pm +++ b/pidl/lib/Parse/Pidl/Typelist.pm @@ -138,8 +138,18 @@ sub resolveType($) my ($ctype) = @_; if (not hasType($ctype)) { - # assume struct typedef - return { TYPE => "TYPEDEF", NAME => $ctype, DATA => { TYPE => "STRUCT" } }; + if (! ref $ctype) { + # it looks like a name. + # assume struct typedef + return { TYPE => "TYPEDEF", NAME => $ctype, DATA => { TYPE => "STRUCT" } }; + } + if ($ctype->{NAME} && ($ctype->{TYPE} eq "STRUCT")) { + return { + TYPE => "TYPEDEF", + NAME => $ctype->{NAME}, + DATA => $ctype + }; + } } else { return getType($ctype); } diff --git a/python/samba/gp/gpclass.py b/python/samba/gp/gpclass.py index 08be472..d86aace 100644 --- a/python/samba/gp/gpclass.py +++ b/python/samba/gp/gpclass.py @@ -805,9 +805,7 @@ def site_dn_for_machine(samdb, dc_hostname, lp, creds, hostname): samlogon_response = ndr_unpack(nbt.netlogon_samlogon_response, bytes(res.msgs[0]['Netlogon'][0])) - if samlogon_response.ntver not in [nbt.NETLOGON_NT_VERSION_5EX, - (nbt.NETLOGON_NT_VERSION_1 - | nbt.NETLOGON_NT_VERSION_5EX)]: + if not (samlogon_response.ntver & nbt.NETLOGON_NT_VERSION_5EX): raise RuntimeError('site_dn_for_machine: Invalid NtVer in ' + 'netlogon_samlogon_response') diff --git a/python/samba/tests/blackbox/misc_dfs_widelink.py b/python/samba/tests/blackbox/misc_dfs_widelink.py new file mode 100644 index 0000000..7948590 --- /dev/null +++ b/python/samba/tests/blackbox/misc_dfs_widelink.py @@ -0,0 +1,86 @@ +# Blackbox tests for DFS (widelink) +# +# Copyright (C) Noel Power noel.power@suse.com +# +# 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 samba.tests import BlackboxTestCase, BlackboxProcessError +from samba.samba3 import param as s3param + +from samba.credentials import Credentials + +import os + +class DfsWidelinkBlockboxTestBase(BlackboxTestCase): + + def setUp(self): + super().setUp() + self.lp = s3param.get_context() + self.server = os.environ["SERVER"] + self.user = os.environ["USER"] + self.passwd = os.environ["PASSWORD"] + self.creds = Credentials() + self.creds.guess(self.lp) + self.creds.set_username(self.user) + self.creds.set_password(self.passwd) + self.testdir = os.getenv("TESTDIR", "msdfs-share-wl") + self.share = os.getenv("SHARE", "msdfs-share-wl") + self.dirpath = os.path.join(os.environ["LOCAL_PATH"],self.testdir) + # allow a custom teardown function to be defined + self.cleanup = None + self.cleanup_args = [] + + def tearDown(self): + try: + if (self.cleanup): + self.cleanup(self.cleanup_args) + except Exception as e: + print("remote remove failed: %s" % str(e)) + + def build_test_cmd(self, cmd, args): + cmd = [cmd, "-U%s%%%s" % (self.user, self.passwd)] + cmd.extend(args) + return cmd + + def test_ci_chdir(self): + parent_dir = "msdfs-src1" + dirs = [parent_dir, parent_dir.upper()] + # try as named dir first then try upper-cased version + for adir in dirs: + smbclient_args = self.build_test_cmd("smbclient", ["//%s/%s" % (self.server, self.share), "-c", "cd %s" % (adir)]) + try: + out_str = self.check_output(smbclient_args) + except BlackboxProcessError as e: + print(str(e)) + self.fail(str(e)) + + def test_nested_chdir(self): + parent_dir = "dfshop1" + child_dir = "dfshop2" + smbclient_args = self.build_test_cmd("smbclient", ["//%s/%s" % (self.server, self.share), "-c", "cd %s/%s" % (parent_dir,child_dir)]) + try: + out_str = self.check_output(smbclient_args) + except BlackboxProcessError as e: + print(str(e)) + self.fail(str(e)) + + def test_enumerate_dfs_link(self): + smbclient_args = self.build_test_cmd("smbclient", ["//%s/%s" % (self.server, self.share), "-c", "dir"]) + try: + out_str = self.check_output(smbclient_args) + except BlackboxProcessError as e: + print(str(e)) + self.fail(str(e)) + out_str = out_str.decode() + self.assertIn("msdfs-src1", out_str) diff --git a/python/samba/tests/dns_base.py b/python/samba/tests/dns_base.py index d320a0e..43a62b1 100644 --- a/python/samba/tests/dns_base.py +++ b/python/samba/tests/dns_base.py @@ -20,6 +20,7 @@ from samba.tests import TestCaseInTempDir from samba.dcerpc import dns, dnsp from samba import gensec, tests from samba import credentials +from samba import NTSTATUSError import struct import samba.ndr as ndr import random @@ -76,6 +77,24 @@ class DNSTest(TestCaseInTempDir): self.assertEqual(p_opcode, opcode, "Expected OPCODE %s, got %s" % (opcode, p_opcode)) + def assert_dns_flags_equals(self, packet, flags): + "Helper function to check opcode" + p_flags = packet.operation & (~(dns.DNS_OPCODE|dns.DNS_RCODE)) + self.assertEqual(p_flags, flags, "Expected FLAGS %02x, got %02x" % + (flags, p_flags)) + + def assert_echoed_dns_error(self, request, response, response_p, rcode): + + request_p = ndr.ndr_pack(request) + + self.assertEqual(response.id, request.id) + self.assert_dns_rcode_equals(response, rcode) + self.assert_dns_opcode_equals(response, request.operation & dns.DNS_OPCODE) + self.assert_dns_flags_equals(response, + (request.operation | dns.DNS_FLAG_REPLY) & (~(dns.DNS_OPCODE|dns.DNS_RCODE))) + self.assertEqual(len(response_p), len(request_p)) + self.assertEqual(response_p[4:], request_p[4:]) + def make_name_packet(self, opcode, qid=None): "Helper creating a dns.name_packet" p = dns.name_packet() @@ -112,6 +131,8 @@ class DNSTest(TestCaseInTempDir): return self.creds.get_realm().lower() def dns_transaction_udp(self, packet, host, + allow_remaining=False, + allow_truncated=False, dump=False, timeout=None): "send a DNS query and read the reply" s = None @@ -128,8 +149,22 @@ class DNSTest(TestCaseInTempDir): recv_packet = s.recv(2048, 0) if dump: print(self.hexdump(recv_packet)) - response = ndr.ndr_unpack(dns.name_packet, recv_packet) + if allow_truncated: + # with allow_remaining + # we add some zero bytes + # in order to also parse truncated + # responses + recv_packet_p = recv_packet + 32*b"\x00" + allow_remaining = True + else: + recv_packet_p = recv_packet + response = ndr.ndr_unpack(dns.name_packet, recv_packet_p, + allow_remaining=allow_remaining) return (response, recv_packet) + except RuntimeError as re: + if s is not None: + s.close() + raise AssertionError(re) finally: if s is not None: s.close() @@ -151,11 +186,26 @@ class DNSTest(TestCaseInTempDir): tcp_packet += send_packet s.sendall(tcp_packet) - recv_packet = s.recv(0xffff + 2, 0) + recv_packet = b'' + length = None + for i in range(0, 2 + 0xffff): + if len(recv_packet) >= 2: + length, = struct.unpack('!H', recv_packet[0:2]) + remaining = 2 + length + else: + remaining = 2 + 12 + remaining -= len(recv_packet) + if remaining == 0: + break + recv_packet += s.recv(remaining, 0) if dump: print(self.hexdump(recv_packet)) response = ndr.ndr_unpack(dns.name_packet, recv_packet[2:]) + except RuntimeError as re: + if s is not None: + s.close() + raise AssertionError(re) finally: if s is not None: s.close() @@ -217,18 +267,41 @@ class DNSTKeyTest(DNSTest): self.creds.set_username(tests.env_get_var_value('USERNAME')) self.creds.set_password(tests.env_get_var_value('PASSWORD')) self.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS) + + self.unpriv_creds = None + self.newrecname = "tkeytsig.%s" % self.get_dns_domain() - def tkey_trans(self, creds=None): + def get_unpriv_creds(self): + if self.unpriv_creds is not None: + return self.unpriv_creds + + self.unpriv_creds = credentials.Credentials() + self.unpriv_creds.guess(self.lp_ctx) + self.unpriv_creds.set_username(tests.env_get_var_value('USERNAME_UNPRIV')) + self.unpriv_creds.set_password(tests.env_get_var_value('PASSWORD_UNPRIV')) + self.unpriv_creds.set_kerberos_state(credentials.MUST_USE_KERBEROS) + + return self.unpriv_creds + + def tkey_trans(self, creds=None, algorithm_name="gss-tsig", + tkey_req_in_answers=False, + expected_rcode=dns.DNS_RCODE_OK): "Do a TKEY transaction and establish a gensec context" if creds is None: creds = self.creds - self.key_name = "%s.%s" % (uuid.uuid4(), self.get_dns_domain()) + mech = 'spnego' + + tkey = {} + tkey['name'] = "%s.%s" % (uuid.uuid4(), self.get_dns_domain()) + tkey['creds'] = creds + tkey['mech'] = mech + tkey['algorithm'] = algorithm_name p = self.make_name_packet(dns.DNS_OPCODE_QUERY) - q = self.make_name_question(self.key_name, + q = self.make_name_question(tkey['name'], dns.DNS_QTYPE_TKEY, dns.DNS_QCLASS_IN) questions = [] @@ -236,30 +309,30 @@ class DNSTKeyTest(DNSTest): self.finish_name_packet(p, questions) r = dns.res_rec() - r.name = self.key_name + r.name = tkey['name'] r.rr_type = dns.DNS_QTYPE_TKEY r.rr_class = dns.DNS_QCLASS_IN r.ttl = 0 r.length = 0xffff rdata = dns.tkey_record() - rdata.algorithm = "gss-tsig" + rdata.algorithm = algorithm_name rdata.inception = int(time.time()) rdata.expiration = int(time.time()) + 60 * 60 rdata.mode = dns.DNS_TKEY_MODE_GSSAPI rdata.error = 0 rdata.other_size = 0 - self.g = gensec.Security.start_client(self.settings) - self.g.set_credentials(creds) - self.g.set_target_service("dns") - self.g.set_target_hostname(self.server) - self.g.want_feature(gensec.FEATURE_SIGN) - self.g.start_mech_by_name("spnego") + tkey['gensec'] = gensec.Security.start_client(self.settings) + tkey['gensec'].set_credentials(creds) + tkey['gensec'].set_target_service("dns") + tkey['gensec'].set_target_hostname(self.server) + tkey['gensec'].want_feature(gensec.FEATURE_SIGN) + tkey['gensec'].start_mech_by_name(tkey['mech']) finished = False client_to_server = b"" - (finished, server_to_client) = self.g.update(client_to_server) + (finished, server_to_client) = tkey['gensec'].update(client_to_server) self.assertFalse(finished) data = [x if isinstance(x, int) else ord(x) for x in list(server_to_client)] @@ -268,56 +341,76 @@ class DNSTKeyTest(DNSTest): r.rdata = rdata additional = [r] - p.arcount = 1 - p.additional = additional + if tkey_req_in_answers: + p.ancount = 1 + p.answers = additional + else: + p.arcount = 1 + p.additional = additional (response, response_packet) =\ self.dns_transaction_tcp(p, self.server_ip) + if expected_rcode != dns.DNS_RCODE_OK: + self.assert_echoed_dns_error(p, response, response_packet, expected_rcode) + return self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) tkey_record = response.answers[0].rdata server_to_client = bytes(tkey_record.key_data) - (finished, client_to_server) = self.g.update(server_to_client) + (finished, client_to_server) = tkey['gensec'].update(server_to_client) self.assertTrue(finished) + self.tkey = tkey + self.verify_packet(response, response_packet) def verify_packet(self, response, response_packet, request_mac=b""): + self.assertEqual(response.arcount, 1) self.assertEqual(response.additional[0].rr_type, dns.DNS_QTYPE_TSIG) + if self.tkey['algorithm'] == "gss-tsig": + gss_tsig = True + else: + gss_tsig = False + + request_mac_len = b"" + if len(request_mac) > 0 and gss_tsig: + request_mac_len = struct.pack('!H', len(request_mac)) + tsig_record = response.additional[0].rdata mac = bytes(tsig_record.mac) + self.assertEqual(tsig_record.original_id, response.id) + self.assertEqual(tsig_record.mac_size, len(mac)) + # Cut off tsig record from dns response packet for MAC verification # and reset additional record count. - key_name_len = len(self.key_name) + 2 - tsig_record_len = len(ndr.ndr_pack(tsig_record)) + key_name_len + 10 - - # convert str/bytes to a list (of string char or int) - # so it can be modified - response_packet_list = [x if isinstance(x, int) else ord(x) for x in response_packet] - del response_packet_list[-tsig_record_len:] - response_packet_list[11] = 0 - - # convert modified list (of string char or int) to str/bytes - response_packet_wo_tsig = bytes(response_packet_list) + response_copy = ndr.ndr_deepcopy(response) + response_copy.arcount = 0 + response_packet_wo_tsig = ndr.ndr_pack(response_copy) fake_tsig = dns.fake_tsig_rec() - fake_tsig.name = self.key_name + fake_tsig.name = self.tkey['name'] fake_tsig.rr_class = dns.DNS_QCLASS_ANY fake_tsig.ttl = 0 fake_tsig.time_prefix = tsig_record.time_prefix fake_tsig.time = tsig_record.time fake_tsig.algorithm_name = tsig_record.algorithm_name fake_tsig.fudge = tsig_record.fudge - fake_tsig.error = 0 - fake_tsig.other_size = 0 + fake_tsig.error = tsig_record.error + fake_tsig.other_size = tsig_record.other_size + fake_tsig.other_data = tsig_record.other_data fake_tsig_packet = ndr.ndr_pack(fake_tsig) - data = request_mac + response_packet_wo_tsig + fake_tsig_packet - self.g.check_packet(data, data, mac) + data = request_mac_len + request_mac + response_packet_wo_tsig + fake_tsig_packet + try: + self.tkey['gensec'].check_packet(data, data, mac) + except NTSTATUSError as nt: + raise AssertionError(nt) - def sign_packet(self, packet, key_name): + def sign_packet(self, packet, key_name, + algorithm_name="gss-tsig", + bad_sig=False): "Sign a packet, calculate a MAC and add TSIG record" packet_data = ndr.ndr_pack(packet) @@ -327,18 +420,35 @@ class DNSTKeyTest(DNSTest): fake_tsig.ttl = 0 fake_tsig.time_prefix = 0 fake_tsig.time = int(time.time()) - fake_tsig.algorithm_name = "gss-tsig" + fake_tsig.algorithm_name = algorithm_name fake_tsig.fudge = 300 fake_tsig.error = 0 fake_tsig.other_size = 0 fake_tsig_packet = ndr.ndr_pack(fake_tsig) data = packet_data + fake_tsig_packet - mac = self.g.sign_packet(data, data) + mac = self.tkey['gensec'].sign_packet(data, data) mac_list = [x if isinstance(x, int) else ord(x) for x in list(mac)] + if bad_sig: + if len(mac) > 8: + mac_list[-8] = mac_list[-8] ^ 0xff + if len(mac) > 7: + mac_list[-7] = ord('b') + if len(mac) > 6: + mac_list[-6] = ord('a') + if len(mac) > 5: + mac_list[-5] = ord('d') + if len(mac) > 4: + mac_list[-4] = ord('m') + if len(mac) > 3: + mac_list[-3] = ord('a') + if len(mac) > 2: + mac_list[-2] = ord('c') + if len(mac) > 1: + mac_list[-1] = mac_list[-1] ^ 0xff rdata = dns.tsig_record() - rdata.algorithm_name = "gss-tsig" + rdata.algorithm_name = algorithm_name rdata.time_prefix = 0 rdata.time = fake_tsig.time rdata.fudge = 300 @@ -363,33 +473,10 @@ class DNSTKeyTest(DNSTest): return mac def bad_sign_packet(self, packet, key_name): - """Add bad signature for a packet by bitflipping - the final byte in the MAC""" - - mac_list = [x if isinstance(x, int) else ord(x) for x in list("badmac")] - - rdata = dns.tsig_record() - rdata.algorithm_name = "gss-tsig" - rdata.time_prefix = 0 - rdata.time = int(time.time()) - rdata.fudge = 300 - rdata.original_id = packet.id - rdata.error = 0 - rdata.other_size = 0 - rdata.mac = mac_list - rdata.mac_size = len(mac_list) + """Add bad signature for a packet by + bitflipping and hardcoding bytes at the end of the MAC""" - r = dns.res_rec() - r.name = key_name - r.rr_type = dns.DNS_QTYPE_TSIG - r.rr_class = dns.DNS_QCLASS_ANY - r.ttl = 0 - r.length = 0xffff - r.rdata = rdata - - additional = [r] - packet.additional = additional - packet.arcount = 1 + return self.sign_packet(packet, key_name, bad_sig=True) def search_record(self, name): p = self.make_name_packet(dns.DNS_OPCODE_QUERY) diff --git a/python/samba/tests/dns_tkey.py b/python/samba/tests/dns_tkey.py index 69af14d..f8417ea 100644 --- a/python/samba/tests/dns_tkey.py +++ b/python/samba/tests/dns_tkey.py @@ -19,6 +19,7 @@ import sys import optparse import samba.getopt as options +import samba.ndr as ndr from samba.dcerpc import dns from samba.tests.subunitrun import SubunitOptions, TestProgram from samba.tests.dns_base import DNSTKeyTest @@ -55,17 +56,34 @@ class TestDNSUpdates(DNSTKeyTest): self.server_ip = server_ip super().setUp() - def test_tkey(self): - "test DNS TKEY handshake" + def test_tkey_gss_tsig(self): + "test DNS TKEY handshake with gss-tsig" self.tkey_trans() + def test_tkey_gss_microsoft_com(self): + "test DNS TKEY handshake with gss.microsoft.com" + + self.tkey_trans(algorithm_name="gss.microsoft.com") + + def test_tkey_invalid_gss_TSIG(self): + "test DNS TKEY handshake with invalid gss-TSIG" + + self.tkey_trans(algorithm_name="gss-TSIG", + expected_rcode=dns.DNS_RCODE_REFUSED) + + def test_tkey_invalid_gss_MICROSOFT_com(self): + "test DNS TKEY handshake with invalid gss.MICROSOFT.com" + + self.tkey_trans(algorithm_name="gss.MICROSOFT.com", + expected_rcode=dns.DNS_RCODE_REFUSED) + def test_update_wo_tsig(self): "test DNS update without TSIG record" p = self.make_update_request() (response, response_p) = self.dns_transaction_udp(p, self.server_ip) - self.assert_dns_rcode_equals(response, dns.DNS_RCODE_REFUSED) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) rcode = self.search_record(self.newrecname) self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) @@ -78,10 +96,7 @@ class TestDNSUpdates(DNSTKeyTest): p = self.make_update_request() self.sign_packet(p, "badkey") (response, response_p) = self.dns_transaction_udp(p, self.server_ip) - self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NOTAUTH) - tsig_record = response.additional[0].rdata - self.assertEqual(tsig_record.error, dns.DNS_RCODE_BADKEY) - self.assertEqual(tsig_record.mac_size, 0) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) rcode = self.search_record(self.newrecname) self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) @@ -92,23 +107,149 @@ class TestDNSUpdates(DNSTKeyTest): self.tkey_trans() p = self.make_update_request() - self.bad_sign_packet(p, self.key_name) + self.bad_sign_packet(p, self.tkey['name']) (response, response_p) = self.dns_transaction_udp(p, self.server_ip) - self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NOTAUTH) - tsig_record = response.additional[0].rdata - self.assertEqual(tsig_record.error, dns.DNS_RCODE_BADSIG) - self.assertEqual(tsig_record.mac_size, 0) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) rcode = self.search_record(self.newrecname) self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) - def test_update_tsig(self): - "test DNS update with correct TSIG record" + def test_update_tsig_bad_algorithm(self): + "test DNS update with a TSIG record with a bad algorithm" self.tkey_trans() + algorithm_name = "gss-TSIG" p = self.make_update_request() - mac = self.sign_packet(p, self.key_name) + mac = self.sign_packet(p, self.tkey['name'], + algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) + + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) + + def test_update_tsig_changed_algorithm1(self): + "test DNS update with a TSIG record with a changed algorithm" + + algorithm_name = "gss-tsig" + self.tkey_trans(algorithm_name=algorithm_name) + + # Now delete the record, it's most likely + # a no-op as it should not be there if the test + # runs the first time + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name'], algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Now do an update with the algorithm_name + # changed in the requests TSIG message. + p = self.make_update_request() + algorithm_name = "gss.microsoft.com" + mac = self.sign_packet(p, self.tkey['name'], + algorithm_name=algorithm_name) + algorithm_name = "gss-tsig" + (response, response_p) = self.dns_transaction_udp(p, self.server_ip, + allow_remaining=True) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Check the record is around + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) + + # Now delete the record, with the original + # algorithm_name used in the tkey exchange + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name'], algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) + + def test_update_tsig_changed_algorithm2(self): + "test DNS update with a TSIG record with a changed algorithm" + + algorithm_name = "gss.microsoft.com" + self.tkey_trans(algorithm_name=algorithm_name) + + # Now delete the record, it's most likely + # a no-op as it should not be there if the test + # runs the first time + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name'], algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Now do an update with the algorithm_name + # changed in the requests TSIG message. + p = self.make_update_request() + algorithm_name = "gss-tsig" + mac = self.sign_packet(p, self.tkey['name'], + algorithm_name=algorithm_name) + algorithm_name = "gss.microsoft.com" + (response, response_p) = self.dns_transaction_udp(p, self.server_ip, + allow_truncated=True) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + response_p_pack = ndr.ndr_pack(response) + if len(response_p_pack) == len(response_p): + self.verify_packet(response, response_p, mac) + else: + pass # Windows bug + + # Check the record is around + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) + + # Now delete the record, with the original + # algorithm_name used in the tkey exchange + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name'], algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) + + def test_update_gss_tsig_tkey_req_additional(self): + "test DNS update with correct gss-tsig record tkey req in additional" + + self.tkey_trans() + + p = self.make_update_request() + mac = self.sign_packet(p, self.tkey['name']) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Check the record is around + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) + + # Now delete the record + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name']) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # check it's gone + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) + + def test_update_gss_tsig_tkey_req_answers(self): + "test DNS update with correct gss-tsig record tsig req in answers" + + self.tkey_trans(tkey_req_in_answers=True) + + p = self.make_update_request() + mac = self.sign_packet(p, self.tkey['name']) (response, response_p) = self.dns_transaction_udp(p, self.server_ip) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.verify_packet(response, response_p, mac) @@ -119,7 +260,66 @@ class TestDNSUpdates(DNSTKeyTest): # Now delete the record p = self.make_update_request(delete=True) - mac = self.sign_packet(p, self.key_name) + mac = self.sign_packet(p, self.tkey['name']) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # check it's gone + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) + + def test_update_gss_microsoft_com_tkey_req_additional(self): + "test DNS update with correct gss.microsoft.com record tsig req in additional" + + algorithm_name = "gss.microsoft.com" + self.tkey_trans(algorithm_name=algorithm_name) + + p = self.make_update_request() + mac = self.sign_packet(p, self.tkey['name'], + algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Check the record is around + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) + + # Now delete the record + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name'], + algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # check it's gone + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) + + def test_update_gss_microsoft_com_tkey_req_answers(self): + "test DNS update with correct gss.microsoft.com record tsig req in answers" + + algorithm_name = "gss.microsoft.com" + self.tkey_trans(algorithm_name=algorithm_name, + tkey_req_in_answers=True) + + p = self.make_update_request() + mac = self.sign_packet(p, self.tkey['name'], + algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Check the record is around + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) + + # Now delete the record + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name'], + algorithm_name=algorithm_name) (response, response_p) = self.dns_transaction_udp(p, self.server_ip) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.verify_packet(response, response_p, mac) @@ -131,35 +331,28 @@ class TestDNSUpdates(DNSTKeyTest): def test_update_tsig_windows(self): "test DNS update with correct TSIG record (follow Windows pattern)" - newrecname = "win" + self.newrecname + p = self.make_update_request() + rr_class = dns.DNS_QCLASS_IN ttl = 1200 - p = self.make_name_packet(dns.DNS_OPCODE_UPDATE) - q = self.make_name_question(self.get_dns_domain(), - dns.DNS_QTYPE_SOA, - dns.DNS_QCLASS_IN) - questions = [] - questions.append(q) - self.finish_name_packet(p, questions) - updates = [] r = dns.res_rec() - r.name = newrecname + r.name = self.newrecname r.rr_type = dns.DNS_QTYPE_A r.rr_class = dns.DNS_QCLASS_ANY r.ttl = 0 r.length = 0 updates.append(r) r = dns.res_rec() - r.name = newrecname + r.name = self.newrecname r.rr_type = dns.DNS_QTYPE_AAAA r.rr_class = dns.DNS_QCLASS_ANY r.ttl = 0 r.length = 0 updates.append(r) r = dns.res_rec() - r.name = newrecname + r.name = self.newrecname r.rr_type = dns.DNS_QTYPE_A r.rr_class = rr_class r.ttl = ttl @@ -171,7 +364,7 @@ class TestDNSUpdates(DNSTKeyTest): prereqs = [] r = dns.res_rec() - r.name = newrecname + r.name = self.newrecname r.rr_type = dns.DNS_QTYPE_CNAME r.rr_class = dns.DNS_QCLASS_NONE r.ttl = 0 @@ -181,21 +374,87 @@ class TestDNSUpdates(DNSTKeyTest): p.answers = prereqs (response, response_p) = self.dns_transaction_udp(p, self.server_ip) - self.assert_dns_rcode_equals(response, dns.DNS_RCODE_REFUSED) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) self.tkey_trans() - mac = self.sign_packet(p, self.key_name) + mac = self.sign_packet(p, self.tkey['name']) (response, response_p) = self.dns_transaction_udp(p, self.server_ip) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.verify_packet(response, response_p, mac) # Check the record is around - rcode = self.search_record(newrecname) + rcode = self.search_record(self.newrecname) self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) # Now delete the record + delete_updates = [] + r = dns.res_rec() + r.name = self.newrecname + r.rr_type = dns.DNS_QTYPE_A + r.rr_class = dns.DNS_QCLASS_NONE + r.ttl = 0 + r.length = 0xffff + r.rdata = "10.1.45.64" + delete_updates.append(r) + p = self.make_update_request(delete=True) + p.nscount = len(delete_updates) + p.nsrecs = delete_updates + mac = self.sign_packet(p, self.tkey['name']) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # check it's gone + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) + + def test_update_tsig_record_access_denied(self): + """test DNS update with a TSIG record where the user does not have + permissions to change the record""" + + self.tkey_trans() + adm_tkey = self.tkey + + # First create the record as admin + p = self.make_update_request() + mac = self.sign_packet(p, self.tkey['name']) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Check the record is around + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) + + # Now update the same values as normal user + # should work without error + self.tkey_trans(creds=self.get_unpriv_creds()) + unpriv_tkey = self.tkey + + p = self.make_update_request() + mac = self.sign_packet(p, self.tkey['name']) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Check the record is still around + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) + + # Now try to delete the record a normal user (should fail) + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name']) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) + + # Check the record is still around + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) + + # Now delete the record as admin + self.tkey = adm_tkey p = self.make_update_request(delete=True) - mac = self.sign_packet(p, self.key_name) + mac = self.sign_packet(p, self.tkey['name']) (response, response_p) = self.dns_transaction_udp(p, self.server_ip) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.verify_packet(response, response_p, mac) diff --git a/python/samba/tests/join.py b/python/samba/tests/join.py index b47bc70..b04cb4a 100644 --- a/python/samba/tests/join.py +++ b/python/samba/tests/join.py @@ -156,7 +156,7 @@ class JoinTestCase(DNSTKeyTest): p.nscount = len(updates) p.nsrecs = updates - mac = self.sign_packet(p, self.key_name) + mac = self.sign_packet(p, self.tkey['name']) (response, response_p) = self.dns_transaction_udp(p, self.server_ip) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.verify_packet(response, response_p, mac) diff --git a/python/samba/tests/ntacls.py b/python/samba/tests/ntacls.py index 0b7963d..6e2adda 100644 --- a/python/samba/tests/ntacls.py +++ b/python/samba/tests/ntacls.py @@ -83,5 +83,5 @@ class NtaclsTests(TestCaseInTempDir): lp = LoadParm() open(self.tempf, 'w').write("empty") lp.set("posix:eadb", os.path.join(self.tempdir, "eadbtest.tdb")) - self.assertRaises(Exception, setntacl, lp, self.tempf, NTACL_SDDL, + self.assertRaises(PermissionError, setntacl, lp, self.tempf, NTACL_SDDL, DOMAIN_SID, self.session_info, "native") diff --git a/script/autobuild.py b/script/autobuild.py index ecec352..7291522 100755 --- a/script/autobuild.py +++ b/script/autobuild.py @@ -892,9 +892,10 @@ tasks = { ("ldb-make", "cd lib/ldb && make"), ("ldb-install", "cd lib/ldb && make install"), - ("nondevel-configure", samba_libs_envvars + " ./configure ${PREFIX}"), + ("nondevel-configure", samba_libs_envvars + " ./configure --vendor-name=autobuild-TEST-STRING --vendor-patch-revision=5 ${PREFIX}"), ("nondevel-make", "make -j"), ("nondevel-check", "./bin/smbd -b | grep WITH_NTVFS_FILESERVER && exit 1; exit 0"), + ("nondevel-check", "./bin/smbd --version | grep -e '-autobuild-TEST-STRING-5' && exit 0; exit 1"), ("nondevel-no-libtalloc", "find ./bin | grep -v 'libtalloc-report' | grep 'libtalloc' && exit 1; exit 0"), ("nondevel-no-libtdb", "find ./bin | grep -v 'libtdb-wrap' | grep 'libtdb' && exit 1; exit 0"), ("nondevel-no-libtevent", "find ./bin | grep -v 'libtevent-util' | grep 'libtevent' && exit 1; exit 0"), diff --git a/selftest/flapping.d/gitlab-setxattr-security b/selftest/flapping.d/gitlab-setxattr-security new file mode 100644 index 0000000..d7d2403 --- /dev/null +++ b/selftest/flapping.d/gitlab-setxattr-security @@ -0,0 +1,18 @@ +# gitlab runners with kernel 5.15.109+ +# allow setxattr() on security.NTACL +# +# It's not clear in detail why there's a difference +# between various systems, one reason could be that +# with selinux inode_owner_or_capable() is used to check +# setxattr() permissions: +# it checks for the fileowner too, as well as CAP_FOWNER. +# Otherwise cap_inode_setxattr() is used, which checks for +# CAP_SYS_ADMIN. +# +# But the kernel doesn't have selinux only apparmor... +# +# test_setntacl_forcenative expects +# PermissionError: [Errno 1] Operation not permitted +# +# So for now we allow this to fail... +^samba.tests.ntacls.samba.tests.ntacls.NtaclsTests.test_setntacl_forcenative.none diff --git a/selftest/knownfail-32bit b/selftest/knownfail-32bit index 2946f3e..8ab625d 100644 --- a/selftest/knownfail-32bit +++ b/selftest/knownfail-32bit @@ -65,14 +65,8 @@ # [171(1386)/261 at 6m24s, 4 errors] samba4.local.charset # UNEXPECTED(failure): samba4.local.charset.strcasecmp(none) # REASON: Exception: Exception: ../../lib/util/charset/tests/charset.c:56: strcasecmp("foo", "bar") was 1 (0x1), expected 4 (0x4): different strings both lower -# UNEXPECTED(failure): samba4.local.charset.strcasecmp_m(none) -# REASON: Exception: Exception: ../../lib/util/charset/tests/charset.c:85: strcasecmp_m(file_iso8859_1, file_utf8) was 1 (0x1), expected 38 (0x26): file.{accented e} -# should differ # UNEXPECTED(failure): samba4.local.charset.strncasecmp(none) # REASON: Exception: Exception: ../../lib/util/charset/tests/charset.c:132: strncasecmp("foo", "bar", 3) was 1 (0x1), expected 4 (0x4): different strings both lower -# UNEXPECTED(failure): samba4.local.charset.strncasecmp_m(none) -# REASON: Exception: Exception: ../../lib/util/charset/tests/charset.c:167: strncasecmp_m(file_iso8859_1, file_utf8, 6) was 1 (0x1), expected 38 (0x26): file.{accent -# ed e} should differ # command: /home/samba/samba.git/bin/smbtorture $LOADLIST --configfile=$SMB_CONF_PATH --option='fss:sequence timeout=1' --maximum-runtime=$SELFTEST_MAXTIME --based # ir=$SELFTEST_TMPDIR --format=subunit --option=torture:progress=no --target=samba4 ncalrpc:localhost local.charset 2>&1 | python3 /home/samba/samba.git/selftest/fi # lter-subunit --fail-on-empty --prefix="samba4.local.charset." --suffix="(none)" @@ -82,9 +76,7 @@ # ERROR: Testsuite[samba4.local.charset] # REASON: Exit code was 1 ^samba4.local.charset.strcasecmp.none -^samba4.local.charset.strcasecmp_m.none ^samba4.local.charset.strncasecmp.none -^samba4.local.charset.strncasecmp_m.none # # [229(2702)/261 at 8m44s, 5 errors] samba.tests.samba_tool.provision_lmdb_size # UNEXPECTED(failure): samba.tests.samba_tool.provision_lmdb_size.samba.tests.samba_tool.provision_lmdb_size.ProvisionLmdbSizeTestCase.test_134217728b(none) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 2d449e4..f2b84b4 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -618,6 +618,7 @@ sub provision_raw_prepare($$$$$$$$$$$$$$) $ctx->{statedir} = "$prefix_abs/statedir"; $ctx->{cachedir} = "$prefix_abs/cachedir"; $ctx->{winbindd_socket_dir} = "$prefix_abs/wbsock"; + $ctx->{nmbd_socket_dir} = "$prefix_abs/nmbsock"; $ctx->{ntp_signd_socket_dir} = "$prefix_abs/ntp_signd_socket"; $ctx->{nsswrap_passwd} = "$ctx->{etcdir}/passwd"; $ctx->{nsswrap_group} = "$ctx->{etcdir}/group"; @@ -774,6 +775,7 @@ sub provision_raw_step1($$) state directory = $ctx->{statedir} cache directory = $ctx->{cachedir} winbindd socket directory = $ctx->{winbindd_socket_dir} + nmbd:socket dir = $ctx->{nmbd_socket_dir} ntp signd socket directory = $ctx->{ntp_signd_socket_dir} winbind separator = / interfaces = $interfaces diff --git a/source3/include/fstring.h b/source3/include/fstring.h new file mode 100644 index 0000000..dfc8f17 --- /dev/null +++ b/source3/include/fstring.h @@ -0,0 +1,27 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2002 by Martin Pool <mbp@samba.org> + + 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/>. +*/ + +#ifndef _SAMBA_FSTRING_H +#define _SAMBA_FSTRING_H + +#ifndef FSTRING_LEN +#define FSTRING_LEN 256 +typedef char fstring[FSTRING_LEN]; +#endif + +#endif /* _SAMBA_FSTRING_H */ diff --git a/source3/include/includes.h b/source3/include/includes.h index 1e7b79b..ee05b93 100644 --- a/source3/include/includes.h +++ b/source3/include/includes.h @@ -237,10 +237,7 @@ enum timestamp_set_resolution { _________)/\\_//(\/(/\)/\//\/\///|_)_______ */ -#ifndef FSTRING_LEN -#define FSTRING_LEN 256 -typedef char fstring[FSTRING_LEN]; -#endif +#include "fstring.h" /* debug.h need to be included before samba_util.h for the macro SMB_ASSERT */ #include "../lib/util/debug.h" diff --git a/source3/include/nameserv.h b/source3/include/nameserv.h index 8fbe5a3..51efe82 100644 --- a/source3/include/nameserv.h +++ b/source3/include/nameserv.h @@ -20,18 +20,6 @@ */ -#define INFO_VERSION "INFO/version" -#define INFO_COUNT "INFO/num_entries" -#define INFO_ID_HIGH "INFO/id_high" -#define INFO_ID_LOW "INFO/id_low" -#define ENTRY_PREFIX "ENTRY/" - -#define PERMANENT_TTL 0 - -/* NTAS uses 2, NT uses 1, WfWg uses 0 */ -#define MAINTAIN_LIST 2 -#define ELECTION_VERSION 1 - #define MAX_DGRAM_SIZE (576) /* tcp/ip datagram limit is 576 bytes */ #define MIN_DGRAM_SIZE 12 @@ -140,12 +128,6 @@ enum netbios_reply_type_code { NMB_QUERY, NMB_STATUS, NMB_REG, NMB_REG_REFRESH, #define NAME_POLL_REFRESH_TIME (5*60) #define NAME_POLL_INTERVAL 15 -/* Workgroup state identifiers. */ -#define AM_POTENTIAL_MASTER_BROWSER(work) ((work)->mst_state == MST_POTENTIAL) -#define AM_LOCAL_MASTER_BROWSER(work) ((work)->mst_state == MST_BROWSER) -#define AM_DOMAIN_MASTER_BROWSER(work) ((work)->dom_state == DOMAIN_MST) -#define AM_DOMAIN_MEMBER(work) ((work)->log_state == LOGON_SRV) - /* Microsoft browser NetBIOS name. */ #define MSBROWSE "\001\002__MSBROWSE__\002" @@ -159,293 +141,33 @@ enum netbios_reply_type_code { NMB_QUERY, NMB_STATUS, NMB_REG, NMB_REG_REFRESH, #define FIND_ANY_NAME 0 #define FIND_SELF_NAME 1 -/* - * The different name types that can be in namelists. - * - * SELF_NAME should only be on the broadcast and unicast subnets. - * LMHOSTS_NAME should only be in the remote_broadcast_subnet. - * REGISTER_NAME, DNS_NAME, DNSFAIL_NAME should only be in the wins_server_subnet. - * WINS_PROXY_NAME should only be on the broadcast subnets. - * PERMANENT_NAME can be on all subnets except remote_broadcast_subnet. - * - */ - -enum name_source {LMHOSTS_NAME, REGISTER_NAME, SELF_NAME, DNS_NAME, - DNSFAIL_NAME, PERMANENT_NAME, WINS_PROXY_NAME}; enum node_type {B_NODE=0, P_NODE=1, M_NODE=2, NBDD_NODE=3}; enum packet_type {NMB_PACKET, DGRAM_PACKET}; -enum master_state { - MST_NONE, - MST_POTENTIAL, - MST_BACKUP, - MST_MSB, - MST_BROWSER, - MST_UNBECOMING_MASTER -}; - -enum domain_state { - DOMAIN_NONE, - DOMAIN_WAIT, - DOMAIN_MST -}; - -enum logon_state { - LOGON_NONE, - LOGON_WAIT, - LOGON_SRV -}; - -struct subnet_record; - -struct nmb_data { - uint16_t nb_flags; /* Netbios flags. */ - int num_ips; /* Number of ip entries. */ - struct in_addr *ip; /* The ip list for this name. */ - - enum name_source source; /* Where the name came from. */ - - time_t death_time; /* The time the record must be removed (do not remove if 0). */ - time_t refresh_time; /* The time the record should be refreshed. */ - - uint64_t id; /* unique id */ - struct in_addr wins_ip; /* the address of the wins server this record comes from */ - - int wins_flags; /* similar to the netbios flags but different ! */ -}; - -/* This structure represents an entry in a local netbios name list. */ -struct name_record { - struct name_record *prev, *next; - struct subnet_record *subnet; - struct nmb_name name; /* The netbios name. */ - struct nmb_data data; /* The netbios data. */ -}; - -/* Browser cache for synchronising browse lists. */ -struct browse_cache_record { - struct browse_cache_record *prev, *next; - unstring lmb_name; - unstring work_group; - struct in_addr ip; - time_t sync_time; - time_t death_time; /* The time the record must be removed. */ -}; - -/* used for server information: client, nameserv and ipc */ -struct server_info_struct { - fstring name; - uint32_t type; - fstring comment; - fstring domain; /* used ONLY in ipc.c NOT namework.c */ - bool server_added; /* used ONLY in ipc.c NOT namework.c */ -}; - -/* This is used to hold the list of servers in my domain, and is - contained within lists of domains. */ - -struct server_record { - struct server_record *next; - struct server_record *prev; - - struct subnet_record *subnet; - - struct server_info_struct serv; - time_t death_time; -}; - -/* A workgroup structure. It contains a list of servers. */ -struct work_record { - struct work_record *next; - struct work_record *prev; - - struct subnet_record *subnet; - - struct server_record *serverlist; - - /* Stage of development from non-local-master up to local-master browser. */ - enum master_state mst_state; - - /* Stage of development from non-domain-master to domain-master browser. */ - enum domain_state dom_state; - - /* Stage of development from non-logon-server to logon server. */ - enum logon_state log_state; - - /* Work group info. */ - unstring work_group; - int token; /* Used when communicating with backup browsers. */ - unstring local_master_browser_name; /* Current local master browser. */ - - /* Announce info. */ - time_t lastannounce_time; - int announce_interval; - bool needannounce; - - /* Timeout time for this workgroup. 0 means permanent. */ - time_t death_time; - - /* Election info */ - bool RunningElection; - bool needelection; - int ElectionCount; - uint32_t ElectionCriterion; - - /* Domain master browser info. Used for efficient syncs. */ - struct nmb_name dmb_name; - struct in_addr dmb_addr; -}; - -/* typedefs needed to define copy & free functions for userdata. */ -struct userdata_struct; - -typedef struct userdata_struct * (*userdata_copy_fn)(struct userdata_struct *); -typedef void (*userdata_free_fn)(struct userdata_struct *); - -/* Structure to define any userdata passed around. */ - -struct userdata_struct { - userdata_copy_fn copy_fn; - userdata_free_fn free_fn; - unsigned int userdata_len; - char data[16]; /* 16 is to ensure alignment/padding on all systems */ +#define MAX_NETBIOSNAME_LEN 16 +/* DOS character, NetBIOS namestring. Type used on the wire. */ +typedef char nstring[MAX_NETBIOSNAME_LEN]; +/* Unix character, NetBIOS namestring. Type used to manipulate name in nmbd. */ +typedef char unstring[MAX_NETBIOSNAME_LEN*4]; + +/* A netbios name structure. */ +struct nmb_name { + nstring name; + char scope[64]; + unsigned int name_type; }; -struct response_record; -struct packet_struct; -struct res_rec; - -/* typedef to define the function called when this response packet comes in. */ -typedef void (*response_function)(struct subnet_record *, struct response_record *, - struct packet_struct *); - -/* typedef to define the function called when this response record times out. */ -typedef void (*timeout_response_function)(struct subnet_record *, - struct response_record *); - -/* typedef to define the function called when the request that caused this - response record to be created is successful. */ -typedef void (*success_function)(struct subnet_record *, struct userdata_struct *, ...); - -/* typedef to define the function called when the request that caused this - response record to be created is unsuccessful. */ -typedef void (*fail_function)(struct subnet_record *, struct response_record *, ...); - -/* List of typedefs for success and fail functions of the different query - types. Used to catch any compile time prototype errors. */ - -typedef void (*register_name_success_function)( struct subnet_record *, - struct userdata_struct *, - struct nmb_name *, - uint16_t, - int, - struct in_addr); -typedef void (*register_name_fail_function)( struct subnet_record *, - struct response_record *, - struct nmb_name *); - -typedef void (*release_name_success_function)( struct subnet_record *, - struct userdata_struct *, - struct nmb_name *, - struct in_addr); -typedef void (*release_name_fail_function)( struct subnet_record *, - struct response_record *, - struct nmb_name *); - -typedef void (*refresh_name_success_function)( struct subnet_record *, - struct userdata_struct *, - struct nmb_name *, - uint16_t, - int, - struct in_addr); -typedef void (*refresh_name_fail_function)( struct subnet_record *, - struct response_record *, - struct nmb_name *); - -typedef void (*query_name_success_function)( struct subnet_record *, - struct userdata_struct *, - struct nmb_name *, - struct in_addr, - struct res_rec *answers); - -typedef void (*query_name_fail_function)( struct subnet_record *, - struct response_record *, - struct nmb_name *, - int); - -typedef void (*node_status_success_function)( struct subnet_record *, - struct userdata_struct *, - struct res_rec *, - struct in_addr); -typedef void (*node_status_fail_function)( struct subnet_record *, - struct response_record *); - -/* Initiated name queries are recorded in this list to track any responses. */ - -struct response_record { - struct response_record *next; - struct response_record *prev; - - uint16_t response_id; - - /* Callbacks for packets received or not. */ - response_function resp_fn; - timeout_response_function timeout_fn; - - /* Callbacks for the request succeeding or not. */ - success_function success_fn; - fail_function fail_fn; - - struct packet_struct *packet; - - struct userdata_struct *userdata; - - int num_msgs; - - time_t repeat_time; - time_t repeat_interval; - int repeat_count; - - /* Recursion protection. */ - bool in_expiration_processing; +/* A netbios node status array element. */ +struct node_status { + nstring name; + unsigned char type; + unsigned char flags; }; -/* A subnet structure. It contains a list of workgroups and netbios names. */ - -/* - B nodes will have their own, totally separate subnet record, with their - own netbios name set. These do NOT interact with other subnet records' - netbios names. -*/ - -enum subnet_type { - NORMAL_SUBNET = 0, /* Subnet listed in interfaces list. */ - UNICAST_SUBNET = 1, /* Subnet for unicast packets. */ - REMOTE_BROADCAST_SUBNET = 2, /* Subnet for remote broadcasts. */ - WINS_SERVER_SUBNET = 3 /* Only created if we are a WINS server. */ -}; - -struct subnet_record { - struct subnet_record *next; - struct subnet_record *prev; - - char *subnet_name; /* For Debug identification. */ - enum subnet_type type; /* To catagorize the subnet. */ - - struct work_record *workgrouplist; /* List of workgroups. */ - struct name_record *namelist; /* List of netbios names. */ - struct response_record *responselist; /* List of responses expected. */ - - bool namelist_changed; - bool work_changed; - - struct in_addr bcast_ip; - struct in_addr mask_ip; - struct in_addr myip; - int nmb_sock; /* socket to listen for unicast 137. */ - int nmb_bcast; /* socket to listen for broadcast 137. */ - int dgram_sock; /* socket to listen for unicast 138. */ - int dgram_bcast; /* socket to listen for broadcast 138. */ +/* The extra info from a NetBIOS node status query */ +struct node_status_extra { + unsigned char mac_addr[6]; + /* There really is more here ... */ }; /* A resource record. */ @@ -564,66 +286,4 @@ struct packet_struct #define ANN_ResetBrowserState 14 #define ANN_LocalMasterAnnouncement 15 - -/* Broadcast packet announcement intervals, in minutes. */ - -/* Attempt to add domain logon and domain master names. */ -#define CHECK_TIME_ADD_DOM_NAMES 5 - -/* Search for master browsers of workgroups samba knows about, - except default. */ -#define CHECK_TIME_MST_BROWSE 5 - -/* Request backup browser announcements from other servers. */ -#define CHECK_TIME_ANNOUNCE_BACKUP 15 - -/* Request host announcements from other servers: min and max of interval. */ -#define CHECK_TIME_MIN_HOST_ANNCE 3 -#define CHECK_TIME_MAX_HOST_ANNCE 12 - -/* Announce as master to WINS server and any Primary Domain Controllers. */ -#define CHECK_TIME_MST_ANNOUNCE 15 - -/* Time between syncs from domain master browser to local master browsers. */ -#define CHECK_TIME_DMB_TO_LMB_SYNC 15 - -/* Do all remote announcements this often. */ -#define REMOTE_ANNOUNCE_INTERVAL 180 - -/* what is the maximum period between name refreshes. Note that this only - affects non-permanent self names (in seconds) */ -#define MAX_REFRESH_TIME (60*20) - -/* The Extinction interval: 4 days, time a node will stay in released state */ -#define EXTINCTION_INTERVAL (4*24*60*60) - -/* The Extinction time-out: 1 day, time a node will stay in deleted state */ -#define EXTINCTION_TIMEOUT (24*60*60) - -/* Macro's to enumerate subnets either with or without - the UNICAST subnet. */ - -extern struct subnet_record *subnetlist; -extern struct subnet_record *unicast_subnet; -extern struct subnet_record *wins_server_subnet; -extern struct subnet_record *remote_broadcast_subnet; - -#define FIRST_SUBNET subnetlist -#define NEXT_SUBNET_EXCLUDING_UNICAST(x) ((x)->next) -#define NEXT_SUBNET_INCLUDING_UNICAST(x) (get_next_subnet_maybe_unicast((x))) - -/* wins replication record used between nmbd and wrepld */ -typedef struct _WINS_RECORD { - char name[17]; - char type; - int nb_flags; - int wins_flags; - uint64_t id; - int num_ips; - struct in_addr ip[25]; - struct in_addr wins_ip; -} WINS_RECORD; - -/* To be removed. */ -enum state_type { TEST }; #endif /* _NAMESERV_H_ */ diff --git a/source3/include/session.h b/source3/include/session.h index 40c25e5..903208e 100644 --- a/source3/include/session.h +++ b/source3/include/session.h @@ -39,6 +39,7 @@ struct sessionid { fstring ip_addr_str; time_t connect_start; uint16_t connection_dialect; + bool authenticated; uint8_t encryption_flags; uint16_t cipher; uint16_t signing; diff --git a/source3/include/smb.h b/source3/include/smb.h index 81d761d..ce18872 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -625,31 +625,7 @@ struct kernel_oplocks_ops { #include "smb_macros.h" -#define MAX_NETBIOSNAME_LEN 16 -/* DOS character, NetBIOS namestring. Type used on the wire. */ -typedef char nstring[MAX_NETBIOSNAME_LEN]; -/* Unix character, NetBIOS namestring. Type used to manipulate name in nmbd. */ -typedef char unstring[MAX_NETBIOSNAME_LEN*4]; - -/* A netbios name structure. */ -struct nmb_name { - nstring name; - char scope[64]; - unsigned int name_type; -}; - -/* A netbios node status array element. */ -struct node_status { - nstring name; - unsigned char type; - unsigned char flags; -}; - -/* The extra info from a NetBIOS node status query */ -struct node_status_extra { - unsigned char mac_addr[6]; - /* There really is more here ... */ -}; +#include "nameserv.h" #define SAFE_NETBIOS_CHARS ". -_" diff --git a/source3/lib/sessionid_tdb.c b/source3/lib/sessionid_tdb.c index 2376fd4..54bb895 100644 --- a/source3/lib/sessionid_tdb.c +++ b/source3/lib/sessionid_tdb.c @@ -24,6 +24,7 @@ #include "session.h" #include "util_tdb.h" #include "smbd/globals.h" +#include "../libcli/security/session.h" struct sessionid_traverse_read_state { int (*fn)(const char *key, struct sessionid *session, @@ -48,11 +49,18 @@ static int sessionid_traverse_read_fn(struct smbXsrv_session_global0 *global, }; if (session_info != NULL) { + enum security_user_level ul; + session.uid = session_info->unix_token->uid; session.gid = session_info->unix_token->gid; strncpy(session.username, session_info->unix_info->unix_name, sizeof(fstring)-1); + + ul = security_session_user_level(session_info, NULL); + if (ul >= SECURITY_USER) { + session.authenticated = true; + } } strncpy(session.remote_machine, diff --git a/source3/lib/util_tdb.c b/source3/lib/util_tdb.c index d85f676..3c7c194 100644 --- a/source3/lib/util_tdb.c +++ b/source3/lib/util_tdb.c @@ -324,11 +324,11 @@ int tdb_data_cmp(TDB_DATA t1, TDB_DATA t2) return 1; } if (t1.dptr == t2.dptr) { - return t1.dsize - t2.dsize; + return NUMERIC_CMP(t1.dsize, t2.dsize); } ret = memcmp(t1.dptr, t2.dptr, MIN(t1.dsize, t2.dsize)); if (ret == 0) { - return t1.dsize - t2.dsize; + return NUMERIC_CMP(t1.dsize, t2.dsize); } return ret; } diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c index f76c566..6c6d23c 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -437,23 +437,23 @@ static char *get_kdc_ip_string(char *mem_ctx, char *kdc_str = NULL; char *canon_sockaddr = NULL; - SMB_ASSERT(pss != NULL); - - canon_sockaddr = print_canonical_sockaddr_with_port(frame, pss); - if (canon_sockaddr == NULL) { - goto out; - } + if (pss != NULL) { + canon_sockaddr = print_canonical_sockaddr_with_port(frame, pss); + if (canon_sockaddr == NULL) { + goto out; + } - kdc_str = talloc_asprintf(frame, - "\t\tkdc = %s\n", - canon_sockaddr); - if (kdc_str == NULL) { - goto out; - } + kdc_str = talloc_asprintf(frame, + "\t\tkdc = %s\n", + canon_sockaddr); + if (kdc_str == NULL) { + goto out; + } - ok = sockaddr_storage_to_samba_sockaddr(&sa, pss); - if (!ok) { - goto out; + ok = sockaddr_storage_to_samba_sockaddr(&sa, pss); + if (!ok) { + goto out; + } } /* @@ -704,7 +704,7 @@ bool create_local_private_krb5_conf_for_domain(const char *realm, return false; } - if (domain == NULL || pss == NULL) { + if (domain == NULL) { return false; } diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c index b5139e5..d467079 100644 --- a/source3/libads/ldap.c +++ b/source3/libads/ldap.c @@ -275,12 +275,12 @@ static bool ads_fill_cldap_reply(ADS_STRUCT *ads, /* Fill in the ads->config values */ + ADS_TALLOC_CONST_FREE(ads->config.workgroup); ADS_TALLOC_CONST_FREE(ads->config.realm); ADS_TALLOC_CONST_FREE(ads->config.bind_path); ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name); ADS_TALLOC_CONST_FREE(ads->config.server_site_name); ADS_TALLOC_CONST_FREE(ads->config.client_site_name); - ADS_TALLOC_CONST_FREE(ads->server.workgroup); if (!check_cldap_reply_required_flags(cldap_reply->server_type, ads->config.flags)) { @@ -296,6 +296,13 @@ static bool ads_fill_cldap_reply(ADS_STRUCT *ads, goto out; } + ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name); + if (ads->config.workgroup == NULL) { + DBG_WARNING("Out of memory\n"); + ret = false; + goto out; + } + ads->config.realm = talloc_asprintf_strupper_m(ads, "%s", cldap_reply->dns_domain); @@ -334,13 +341,6 @@ static bool ads_fill_cldap_reply(ADS_STRUCT *ads, } } - ads->server.workgroup = talloc_strdup(ads, cldap_reply->domain_name); - if (ads->server.workgroup == NULL) { - DBG_WARNING("Out of memory\n"); - ret = false; - goto out; - } - ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT; ads->ldap.ss = *ss; diff --git a/source3/librpc/idl/ads.idl b/source3/librpc/idl/ads.idl index 4f3a387..d10e5b4 100644 --- a/source3/librpc/idl/ads.idl +++ b/source3/librpc/idl/ads.idl @@ -59,6 +59,7 @@ interface ads typedef [nopull,nopush] struct { nbt_server_type flags; /* cldap flags identifying the services. */ + string workgroup; string realm; string bind_path; string ldap_server_name; diff --git a/source3/libsmb/clidgram.c b/source3/libsmb/clidgram.c index a45bdac..c87c870 100644 --- a/source3/libsmb/clidgram.c +++ b/source3/libsmb/clidgram.c @@ -349,7 +349,11 @@ struct tevent_req *nbt_getdc_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } - subreq = nb_packet_reader_send(state, ev, DGRAM_PACKET, -1, + subreq = nb_packet_reader_send(state, + ev, + global_nmbd_socket_dir(), + DGRAM_PACKET, + -1, state->my_mailslot); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); diff --git a/source3/libsmb/dsgetdcname.c b/source3/libsmb/dsgetdcname.c index 09a6e66..654893c 100644 --- a/source3/libsmb/dsgetdcname.c +++ b/source3/libsmb/dsgetdcname.c @@ -196,7 +196,29 @@ static NTSTATUS store_cldap_reply(TALLOC_CTX *mem_ctx, /* FIXME */ r->sockaddr_size = 0x10; /* the w32 winsock addr size */ r->sockaddr.sockaddr_family = 2; /* AF_INET */ - r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, addr); + if (is_ipaddress_v4(addr)) { + r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, addr); + if (r->sockaddr.pdc_ip == NULL) { + return NT_STATUS_NO_MEMORY; + } + } else { + /* + * ndr_push_NETLOGON_SAM_LOGON_RESPONSE_EX will + * fail with an ipv6 address. + * + * This matches windows behaviour in the CLDAP + * response when NETLOGON_NT_VERSION_5EX_WITH_IP + * is used. + * + * Windows returns the ipv4 address of the ipv6 + * server interface and falls back to 127.0.0.1 + * if there's no ipv4 address. + */ + r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, "127.0.0.1"); + if (r->sockaddr.pdc_ip == NULL) { + return NT_STATUS_NO_MEMORY; + } + } ndr_err = ndr_push_struct_blob(&blob, mem_ctx, r, (ndr_push_flags_fn_t)ndr_push_NETLOGON_SAM_LOGON_RESPONSE_EX); @@ -930,6 +952,11 @@ static NTSTATUS process_dc_netbios(TALLOC_CTX *mem_ctx, name_type = NBT_NAME_PDC; } + /* + * It's 2024 we always want an AD style response! + */ + nt_version |= NETLOGON_NT_VERSION_AVOID_NT4EMUL; + nt_version |= map_ds_flags_to_nt_version(flags); snprintf(my_acct_name, diff --git a/source3/libsmb/libsmb_xattr.c b/source3/libsmb/libsmb_xattr.c index dcb2f9e..a902341 100644 --- a/source3/libsmb/libsmb_xattr.c +++ b/source3/libsmb/libsmb_xattr.c @@ -121,7 +121,13 @@ ace_compare(struct security_ace *ace1, */ if (ace1->type != ace2->type) { - return ace2->type - ace1->type; + /* + * ace2 and ace1 are reversed here, so that + * ACCESS_DENIED_ACE_TYPE (1) sorts before + * ACCESS_ALLOWED_ACE_TYPE (0), which is the order you + * usually want. + */ + return NUMERIC_CMP(ace2->type, ace1->type); } if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) { @@ -129,15 +135,15 @@ ace_compare(struct security_ace *ace1, } if (ace1->flags != ace2->flags) { - return ace1->flags - ace2->flags; + return NUMERIC_CMP(ace1->flags, ace2->flags); } if (ace1->access_mask != ace2->access_mask) { - return ace1->access_mask - ace2->access_mask; + return NUMERIC_CMP(ace1->access_mask, ace2->access_mask); } if (ace1->size != ace2->size) { - return ace1->size - ace2->size; + return NUMERIC_CMP(ace1->size, ace2->size); } return memcmp(ace1, ace2, sizeof(struct security_ace)); diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c index e6c0c7d..8f6a9b5 100644 --- a/source3/libsmb/namequery.c +++ b/source3/libsmb/namequery.c @@ -34,6 +34,7 @@ #include "lib/gencache.h" #include "librpc/gen_ndr/dns.h" #include "lib/util/util_net.h" +#include "lib/util/tsort.h" #include "lib/util/string_wrappers.h" /* nmbd.c sets this to True. */ @@ -644,7 +645,12 @@ static struct tevent_req *nb_trans_send( return tevent_req_post(req, ev); } - subreq = nb_packet_reader_send(state, ev, type, state->trn_id, NULL); + subreq = nb_packet_reader_send(state, + ev, + global_nmbd_socket_dir(), + type, + state->trn_id, + NULL); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } @@ -1082,8 +1088,15 @@ bool name_status_find(const char *q_name, } /* - comparison function used by sort_addr_list -*/ + * comparison function used by sort_addr_list + * + * This comparison is intransitive in sort if a socket has an invalid + * family (i.e., not IPv4 or IPv6), or an interface doesn't support + * the family. Say we have sockaddrs with IP versions {4,5,6}, of + * which 5 is invalid. By this function, 4 == 5 and 6 == 5, but 4 != + * 6. This is of course a consequence of cmp() being unable to + * communicate error. + */ static int addr_compare(const struct sockaddr_storage *ss1, const struct sockaddr_storage *ss2) @@ -1171,7 +1184,7 @@ static int addr_compare(const struct sockaddr_storage *ss1, max_bits2 += 128; } } - return max_bits2 - max_bits1; + return NUMERIC_CMP(max_bits2, max_bits1); } /* diff --git a/source3/libsmb/nmblib.c b/source3/libsmb/nmblib.c index c90e92e..2297dd9 100644 --- a/source3/libsmb/nmblib.c +++ b/source3/libsmb/nmblib.c @@ -23,6 +23,12 @@ #include "libsmb/nmblib.h" #include "lib/util/string_wrappers.h" +const char *global_nmbd_socket_dir(void) +{ + return lp_parm_const_string(-1, "nmbd", "socket dir", + get_dyn_NMBDSOCKETDIR()); +} + static const struct opcode_names { const char *nmb_opcode_name; int opcode; @@ -1229,8 +1235,10 @@ static unsigned char sort_ip[4]; static int name_query_comp(unsigned char *p1, unsigned char *p2) { - return matching_len_bits(p2+2, sort_ip, 4) - - matching_len_bits(p1+2, sort_ip, 4); + int a = matching_len_bits(p1+2, sort_ip, 4); + int b = matching_len_bits(p2+2, sort_ip, 4); + /* reverse sort -- p2 derived value comes first */ + return NUMERIC_CMP(b, a); } /**************************************************************************** diff --git a/source3/libsmb/nmblib.h b/source3/libsmb/nmblib.h index 52600a4..5171a26 100644 --- a/source3/libsmb/nmblib.h +++ b/source3/libsmb/nmblib.h @@ -29,6 +29,8 @@ /* The following definitions come from libsmb/nmblib.c */ +const char *global_nmbd_socket_dir(void); + void debug_nmb_packet(struct packet_struct *p); void put_name(char *dest, const char *name, int pad, unsigned int name_type); char *nmb_namestr(const struct nmb_name *n); diff --git a/source3/libsmb/unexpected.c b/source3/libsmb/unexpected.c index b81d379..10ceac7 100644 --- a/source3/libsmb/unexpected.c +++ b/source3/libsmb/unexpected.c @@ -26,12 +26,6 @@ #include "lib/tsocket/tsocket.h" #include "lib/util/sys_rw.h" -static const char *nmbd_socket_dir(void) -{ - return lp_parm_const_string(-1, "nmbd", "socket dir", - get_dyn_NMBDSOCKETDIR()); -} - struct nb_packet_query { enum packet_type type; size_t mailslot_namelen; @@ -74,6 +68,7 @@ static void nb_packet_server_listener(struct tevent_context *ev, NTSTATUS nb_packet_server_create(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *nmbd_socket_dir, int max_clients, struct nb_packet_server **presult) { @@ -90,7 +85,7 @@ NTSTATUS nb_packet_server_create(TALLOC_CTX *mem_ctx, result->max_clients = max_clients; result->listen_sock = create_pipe_sock( - nmbd_socket_dir(), "unexpected", 0755); + nmbd_socket_dir, "unexpected", 0755); if (result->listen_sock == -1) { status = map_nt_error_from_unix(errno); goto fail; @@ -248,7 +243,7 @@ static void nb_packet_got_query(struct tevent_req *req) ssize_t nread; int err; - nread = tstream_read_packet_recv(req, talloc_tos(), &buf, &err); + nread = tstream_read_packet_recv(req, client, &buf, &err); TALLOC_FREE(req); if (nread < (ssize_t)sizeof(struct nb_packet_query)) { DEBUG(10, ("read_packet_recv returned %d (%s)\n", @@ -280,6 +275,8 @@ static void nb_packet_got_query(struct tevent_req *req) } } + TALLOC_FREE(buf); + client->ack.byte = 0; client->ack.iov[0].iov_base = &client->ack.byte; client->ack.iov[0].iov_len = 1; @@ -333,7 +330,7 @@ static void nb_packet_client_read_done(struct tevent_req *req) uint8_t *buf; int err; - nread = tstream_read_packet_recv(req, talloc_tos(), &buf, &err); + nread = tstream_read_packet_recv(req, client, &buf, &err); TALLOC_FREE(req); if (nread == 1) { DEBUG(10, ("Protocol error, received data on write-only " @@ -495,6 +492,7 @@ static void nb_packet_reader_got_ack(struct tevent_req *subreq); struct tevent_req *nb_packet_reader_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *nmbd_socket_dir, enum packet_type type, int trn_id, const char *mailslot_name) @@ -530,7 +528,7 @@ struct tevent_req *nb_packet_reader_send(TALLOC_CTX *mem_ctx, tevent_req_nterror(req, map_nt_error_from_unix(errno)); return tevent_req_post(req, ev); } - rpath = talloc_asprintf(state, "%s/%s", nmbd_socket_dir(), + rpath = talloc_asprintf(state, "%s/%s", nmbd_socket_dir, "unexpected"); if (tevent_req_nomem(rpath, req)) { return tevent_req_post(req, ev); diff --git a/source3/libsmb/unexpected.h b/source3/libsmb/unexpected.h index 270976b..4ae9b20 100644 --- a/source3/libsmb/unexpected.h +++ b/source3/libsmb/unexpected.h @@ -29,12 +29,14 @@ struct nb_packet_reader; NTSTATUS nb_packet_server_create(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *nmbd_socket_dir, int max_clients, struct nb_packet_server **presult); void nb_packet_dispatch(struct nb_packet_server *server, struct packet_struct *p); struct tevent_req *nb_packet_reader_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *nmbd_socket_dir, enum packet_type type, int trn_id, const char *mailslot_name); diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c index 905da04..328a9bf 100644 --- a/source3/locking/brlock.c +++ b/source3/locking/brlock.c @@ -408,12 +408,9 @@ static int lock_compare(const struct lock_struct *lck1, const struct lock_struct *lck2) { if (lck1->start != lck2->start) { - return (lck1->start - lck2->start); + return NUMERIC_CMP(lck1->start, lck2->start); } - if (lck2->size != lck1->size) { - return ((int)lck1->size - (int)lck2->size); - } - return 0; + return NUMERIC_CMP(lck1->size, lck2->size); } #endif diff --git a/source3/modules/posixacl_xattr.c b/source3/modules/posixacl_xattr.c index 365cdc7..5d0516c 100644 --- a/source3/modules/posixacl_xattr.c +++ b/source3/modules/posixacl_xattr.c @@ -226,14 +226,14 @@ static int posixacl_xattr_entry_compare(const void *left, const void *right) tag_left = SVAL(left, 0); tag_right = SVAL(right, 0); - ret = (tag_left - tag_right); - if (!ret) { + ret = NUMERIC_CMP(tag_left, tag_right); + if (ret == 0) { /* ID is the third element in the entry, after two short integers (tag and perm), i.e at offset 4. */ id_left = IVAL(left, 4); id_right = IVAL(right, 4); - ret = id_left - id_right; + ret = NUMERIC_CMP(id_left, id_right); } return ret; diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index 62ad506..7380598 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -2147,6 +2147,12 @@ static struct tevent_req *vfswrap_offload_write_send( .remaining = to_copy, }; + status = vfs_offload_token_ctx_init(handle->conn->sconn->client, + &vfswrap_offload_ctx); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + tevent_req_set_cleanup_fn(req, vfswrap_offload_write_cleanup); switch (fsctl) { diff --git a/source3/modules/vfs_recycle.c b/source3/modules/vfs_recycle.c index 327a7ee..ea0417d 100644 --- a/source3/modules/vfs_recycle.c +++ b/source3/modules/vfs_recycle.c @@ -55,10 +55,14 @@ static int vfs_recycle_connect(struct vfs_handle_struct *handle, const char *service, const char *user) { + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); struct recycle_config_data *config = NULL; int ret; int t; - const char *buff; + const char *buff = NULL; + const char **tmplist = NULL; + char *repository = NULL; ret = SMB_VFS_NEXT_CONNECT(handle, service, user); if (ret < 0) { @@ -75,10 +79,30 @@ static int vfs_recycle_connect(struct vfs_handle_struct *handle, errno = ENOMEM; return -1; } - config->repository = lp_parm_const_string(SNUM(handle->conn), - "recycle", - "repository", - ".recycle"); + buff = lp_parm_const_string(SNUM(handle->conn), + "recycle", + "repository", + ".recycle"); + repository = talloc_sub_full( + config, + lp_servicename(talloc_tos(), lp_sub, SNUM(handle->conn)), + handle->conn->session_info->unix_info->unix_name, + handle->conn->connectpath, + handle->conn->session_info->unix_token->gid, + handle->conn->session_info->unix_info->sanitized_username, + handle->conn->session_info->info->domain_name, + buff); + if (repository == NULL) { + DBG_ERR("talloc_sub_full() failed\n"); + TALLOC_FREE(config); + errno = ENOMEM; + return -1; + } + /* shouldn't we allow absolute path names here? --metze */ + /* Yes :-). JRA. */ + trim_char(repository, '\0', '/'); + config->repository = repository; + config->keeptree = lp_parm_bool(SNUM(handle->conn), "recycle", "keeptree", @@ -95,18 +119,48 @@ static int vfs_recycle_connect(struct vfs_handle_struct *handle, "recycle", "touch_mtime", False); - config->exclude = lp_parm_string_list(SNUM(handle->conn), - "recycle", - "exclude", - NULL); - config->exclude_dir = lp_parm_string_list(SNUM(handle->conn), - "recycle", - "exclude_dir", - NULL); - config->noversions = lp_parm_string_list(SNUM(handle->conn), - "recycle", - "noversions", - NULL); + tmplist = lp_parm_string_list(SNUM(handle->conn), + "recycle", + "exclude", + NULL); + if (tmplist != NULL) { + char **tmpcpy = str_list_copy(config, tmplist); + if (tmpcpy == NULL) { + DBG_ERR("str_list_copy() failed\n"); + TALLOC_FREE(config); + errno = ENOMEM; + return -1; + } + config->exclude = discard_const_p(const char *, tmpcpy); + } + tmplist = lp_parm_string_list(SNUM(handle->conn), + "recycle", + "exclude_dir", + NULL); + if (tmplist != NULL) { + char **tmpcpy = str_list_copy(config, tmplist); + if (tmpcpy == NULL) { + DBG_ERR("str_list_copy() failed\n"); + TALLOC_FREE(config); + errno = ENOMEM; + return -1; + } + config->exclude_dir = discard_const_p(const char *, tmpcpy); + } + tmplist = lp_parm_string_list(SNUM(handle->conn), + "recycle", + "noversions", + NULL); + if (tmplist != NULL) { + char **tmpcpy = str_list_copy(config, tmplist); + if (tmpcpy == NULL) { + DBG_ERR("str_list_copy() failed\n"); + TALLOC_FREE(config); + errno = ENOMEM; + return -1; + } + config->noversions = discard_const_p(const char *, tmpcpy); + } config->minsize = conv_str_size(lp_parm_const_string( SNUM(handle->conn), "recycle", "minsize", NULL)); config->maxsize = conv_str_size(lp_parm_const_string( @@ -421,42 +475,27 @@ static int recycle_unlink_internal(vfs_handle_struct *handle, const struct smb_filename *smb_fname, int flags) { - const struct loadparm_substitution *lp_sub = - loadparm_s3_global_substitution(); - connection_struct *conn = handle->conn; + TALLOC_CTX *frame = NULL; struct smb_filename *full_fname = NULL; char *path_name = NULL; - char *temp_name = NULL; - char *final_name = NULL; + const char *temp_name = NULL; + const char *final_name = NULL; struct smb_filename *smb_fname_final = NULL; - const char *base; - char *repository = NULL; + const char *base = NULL; int i = 1; off_t file_size; /* space_avail; */ bool exist; int rc = -1; - struct recycle_config_data *config; + struct recycle_config_data *config = NULL; SMB_VFS_HANDLE_GET_DATA(handle, config, struct recycle_config_data, - return true); + return -1); - repository = talloc_sub_full( - NULL, - lp_servicename(talloc_tos(), lp_sub, SNUM(conn)), - conn->session_info->unix_info->unix_name, - conn->connectpath, - conn->session_info->unix_token->gid, - conn->session_info->unix_info->sanitized_username, - conn->session_info->info->domain_name, - config->repository); - ALLOC_CHECK(repository, done); - /* shouldn't we allow absolute path names here? --metze */ - /* Yes :-). JRA. */ - trim_char(repository, '\0', '/'); + frame = talloc_stackframe(); - if(!repository || *(repository) == '\0') { + if (config->repository[0] == '\0') { DEBUG(3, ("recycle: repository path not set, purging %s...\n", smb_fname_str_dbg(smb_fname))); rc = SMB_VFS_NEXT_UNLINKAT(handle, @@ -466,16 +505,18 @@ static int recycle_unlink_internal(vfs_handle_struct *handle, goto done; } - full_fname = full_path_from_dirfsp_atname(talloc_tos(), + full_fname = full_path_from_dirfsp_atname(frame, dirfsp, smb_fname); if (full_fname == NULL) { - return -1; + rc = -1; + errno = ENOMEM; + goto done; } /* we don't recycle the recycle bin... */ - if (strncmp(full_fname->base_name, repository, - strlen(repository)) == 0) { + if (strncmp(full_fname->base_name, config->repository, + strlen(config->repository)) == 0) { DEBUG(3, ("recycle: File is within recycling bin, unlinking ...\n")); rc = SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, @@ -539,7 +580,7 @@ static int recycle_unlink_internal(vfs_handle_struct *handle, */ /* extract filename and path */ - if (!parent_dirname(talloc_tos(), full_fname->base_name, &path_name, &base)) { + if (!parent_dirname(frame, full_fname->base_name, &path_name, &base)) { rc = -1; errno = ENOMEM; goto done; @@ -571,13 +612,16 @@ static int recycle_unlink_internal(vfs_handle_struct *handle, } if (config->keeptree) { - if (asprintf(&temp_name, "%s/%s", repository, path_name) == -1) { - ALLOC_CHECK(temp_name, done); + temp_name = talloc_asprintf(frame, "%s/%s", + config->repository, + path_name); + if (temp_name == NULL) { + rc = -1; + goto done; } } else { - temp_name = SMB_STRDUP(repository); + temp_name = config->repository; } - ALLOC_CHECK(temp_name, done); exist = recycle_directory_exist(handle, temp_name); if (exist) { @@ -600,12 +644,15 @@ static int recycle_unlink_internal(vfs_handle_struct *handle, } } - if (asprintf(&final_name, "%s/%s", temp_name, base) == -1) { - ALLOC_CHECK(final_name, done); + final_name = talloc_asprintf(frame, "%s/%s", + temp_name, base); + if (final_name == NULL) { + rc = -1; + goto done; } /* Create smb_fname with final base name and orig stream name. */ - smb_fname_final = synthetic_smb_fname(talloc_tos(), + smb_fname_final = synthetic_smb_fname(frame, final_name, full_fname->stream_name, NULL, @@ -641,20 +688,16 @@ static int recycle_unlink_internal(vfs_handle_struct *handle, /* rename file we move to recycle bin */ i = 1; while (recycle_file_exist(handle, smb_fname_final)) { - SAFE_FREE(final_name); - if (asprintf(&final_name, "%s/Copy #%d of %s", temp_name, i++, base) == -1) { - ALLOC_CHECK(final_name, done); - } + char *copy = NULL; + TALLOC_FREE(smb_fname_final->base_name); - smb_fname_final->base_name = talloc_strdup(smb_fname_final, - final_name); - if (smb_fname_final->base_name == NULL) { - rc = SMB_VFS_NEXT_UNLINKAT(handle, - dirfsp, - smb_fname, - flags); + copy = talloc_asprintf(smb_fname_final, "%s/Copy #%d of %s", + temp_name, i++, base); + if (copy == NULL) { + rc = -1; goto done; } + smb_fname_final->base_name = copy; } DEBUG(10, ("recycle: Moving %s to %s\n", smb_fname_str_dbg(full_fname), @@ -681,12 +724,7 @@ static int recycle_unlink_internal(vfs_handle_struct *handle, recycle_do_touch(handle, smb_fname_final, config->touch_mtime); done: - TALLOC_FREE(path_name); - SAFE_FREE(temp_name); - SAFE_FREE(final_name); - TALLOC_FREE(full_fname); - TALLOC_FREE(smb_fname_final); - TALLOC_FREE(repository); + TALLOC_FREE(frame); return rc; } diff --git a/source3/modules/vfs_vxfs.c b/source3/modules/vfs_vxfs.c index aae2ca1..ecc53d0 100644 --- a/source3/modules/vfs_vxfs.c +++ b/source3/modules/vfs_vxfs.c @@ -111,13 +111,13 @@ static int vxfs_ace_cmp(const void *ace1, const void *ace2) type_a1 = SVAL(ace1, 0); type_a2 = SVAL(ace2, 0); - ret = (type_a1 - type_a2); - if (!ret) { + ret = NUMERIC_CMP(type_a1, type_a2); + if (ret == 0) { /* Compare ID under type */ /* skip perm thus take offset as 4*/ id_a1 = IVAL(ace1, 4); id_a2 = IVAL(ace2, 4); - ret = id_a1 - id_a2; + ret = NUMERIC_CMP(id_a1, id_a2); } return ret; diff --git a/source3/modules/vfs_widelinks.c b/source3/modules/vfs_widelinks.c index c5b5084..4339f6d 100644 --- a/source3/modules/vfs_widelinks.c +++ b/source3/modules/vfs_widelinks.c @@ -383,8 +383,17 @@ static int widelinks_openat(vfs_handle_struct *handle, } lstat_ret = SMB_VFS_NEXT_LSTAT(handle, full_fname); - if (lstat_ret != -1 && - VALID_STAT(full_fname->st) && + if (lstat_ret == -1) { + /* + * Path doesn't exist. We must + * return errno from LSTAT. + */ + int saved_errno = errno; + TALLOC_FREE(full_fname); + errno = saved_errno; + return -1; + } + if (VALID_STAT(full_fname->st) && S_ISLNK(full_fname->st.st_ex_mode)) { fsp->fsp_name->st = full_fname->st; } diff --git a/source3/nmbd/nmbd.h b/source3/nmbd/nmbd.h index f207eb9..0a8e345 100644 --- a/source3/nmbd/nmbd.h +++ b/source3/nmbd/nmbd.h @@ -26,6 +26,388 @@ #endif #include "libsmb/nmblib.h" + +#define INFO_VERSION "INFO/version" +#define INFO_COUNT "INFO/num_entries" +#define INFO_ID_HIGH "INFO/id_high" +#define INFO_ID_LOW "INFO/id_low" +#define ENTRY_PREFIX "ENTRY/" + +#define PERMANENT_TTL 0 + +/* NTAS uses 2, NT uses 1, WfWg uses 0 */ +#define MAINTAIN_LIST 2 +#define ELECTION_VERSION 1 + +#define REFRESH_TIME (15*60) +#define NAME_POLL_REFRESH_TIME (5*60) +#define NAME_POLL_INTERVAL 15 + +/* Workgroup state identifiers. */ +#define AM_POTENTIAL_MASTER_BROWSER(work) ((work)->mst_state == MST_POTENTIAL) +#define AM_LOCAL_MASTER_BROWSER(work) ((work)->mst_state == MST_BROWSER) +#define AM_DOMAIN_MASTER_BROWSER(work) ((work)->dom_state == DOMAIN_MST) +#define AM_DOMAIN_MEMBER(work) ((work)->log_state == LOGON_SRV) + +/* Microsoft browser NetBIOS name. */ +#define MSBROWSE "\001\002__MSBROWSE__\002" + +/* Mail slots. */ +#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE" +#define NET_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NETLOGON" +#define NT_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NTLOGON" +#define LANMAN_MAILSLOT "\\MAILSLOT\\LANMAN" + +/* Samba definitions for find_name_on_subnet(). */ +#define FIND_ANY_NAME 0 +#define FIND_SELF_NAME 1 + +/* + * The different name types that can be in namelists. + * + * SELF_NAME should only be on the broadcast and unicast subnets. + * LMHOSTS_NAME should only be in the remote_broadcast_subnet. + * REGISTER_NAME, DNS_NAME, DNSFAIL_NAME should only be in the wins_server_subnet. + * WINS_PROXY_NAME should only be on the broadcast subnets. + * PERMANENT_NAME can be on all subnets except remote_broadcast_subnet. + * + */ + +enum name_source {LMHOSTS_NAME, REGISTER_NAME, SELF_NAME, DNS_NAME, + DNSFAIL_NAME, PERMANENT_NAME, WINS_PROXY_NAME}; + +enum master_state { + MST_NONE, + MST_POTENTIAL, + MST_BACKUP, + MST_MSB, + MST_BROWSER, + MST_UNBECOMING_MASTER +}; + +enum domain_state { + DOMAIN_NONE, + DOMAIN_WAIT, + DOMAIN_MST +}; + +enum logon_state { + LOGON_NONE, + LOGON_WAIT, + LOGON_SRV +}; + +struct subnet_record; + +struct nmb_data { + uint16_t nb_flags; /* Netbios flags. */ + int num_ips; /* Number of ip entries. */ + struct in_addr *ip; /* The ip list for this name. */ + + enum name_source source; /* Where the name came from. */ + + time_t death_time; /* The time the record must be removed (do not remove if 0). */ + time_t refresh_time; /* The time the record should be refreshed. */ + + uint64_t id; /* unique id */ + struct in_addr wins_ip; /* the address of the wins server this record comes from */ + + int wins_flags; /* similar to the netbios flags but different ! */ +}; + +/* This structure represents an entry in a local netbios name list. */ +struct name_record { + struct name_record *prev, *next; + struct subnet_record *subnet; + struct nmb_name name; /* The netbios name. */ + struct nmb_data data; /* The netbios data. */ +}; + +/* Browser cache for synchronising browse lists. */ +struct browse_cache_record { + struct browse_cache_record *prev, *next; + unstring lmb_name; + unstring work_group; + struct in_addr ip; + time_t sync_time; + time_t death_time; /* The time the record must be removed. */ +}; + +/* used for server information: client, nameserv and ipc */ +struct server_info_struct { + fstring name; + uint32_t type; + fstring comment; + fstring domain; /* used ONLY in ipc.c NOT namework.c */ + bool server_added; /* used ONLY in ipc.c NOT namework.c */ +}; + +/* This is used to hold the list of servers in my domain, and is + contained within lists of domains. */ + +struct server_record { + struct server_record *next; + struct server_record *prev; + + struct subnet_record *subnet; + + struct server_info_struct serv; + time_t death_time; +}; + +/* A workgroup structure. It contains a list of servers. */ +struct work_record { + struct work_record *next; + struct work_record *prev; + + struct subnet_record *subnet; + + struct server_record *serverlist; + + /* Stage of development from non-local-master up to local-master browser. */ + enum master_state mst_state; + + /* Stage of development from non-domain-master to domain-master browser. */ + enum domain_state dom_state; + + /* Stage of development from non-logon-server to logon server. */ + enum logon_state log_state; + + /* Work group info. */ + unstring work_group; + int token; /* Used when communicating with backup browsers. */ + unstring local_master_browser_name; /* Current local master browser. */ + + /* Announce info. */ + time_t lastannounce_time; + int announce_interval; + bool needannounce; + + /* Timeout time for this workgroup. 0 means permanent. */ + time_t death_time; + + /* Election info */ + bool RunningElection; + bool needelection; + int ElectionCount; + uint32_t ElectionCriterion; + + /* Domain master browser info. Used for efficient syncs. */ + struct nmb_name dmb_name; + struct in_addr dmb_addr; +}; + +/* typedefs needed to define copy & free functions for userdata. */ +struct userdata_struct; + +typedef struct userdata_struct * (*userdata_copy_fn)(struct userdata_struct *); +typedef void (*userdata_free_fn)(struct userdata_struct *); + +/* Structure to define any userdata passed around. */ + +struct userdata_struct { + userdata_copy_fn copy_fn; + userdata_free_fn free_fn; + unsigned int userdata_len; + char data[16]; /* 16 is to ensure alignment/padding on all systems */ +}; + +struct response_record; +struct packet_struct; +struct res_rec; + +/* typedef to define the function called when this response packet comes in. */ +typedef void (*response_function)(struct subnet_record *, struct response_record *, + struct packet_struct *); + +/* typedef to define the function called when this response record times out. */ +typedef void (*timeout_response_function)(struct subnet_record *, + struct response_record *); + +/* typedef to define the function called when the request that caused this + response record to be created is successful. */ +typedef void (*success_function)(struct subnet_record *, struct userdata_struct *, ...); + +/* typedef to define the function called when the request that caused this + response record to be created is unsuccessful. */ +typedef void (*fail_function)(struct subnet_record *, struct response_record *, ...); + +/* List of typedefs for success and fail functions of the different query + types. Used to catch any compile time prototype errors. */ + +typedef void (*register_name_success_function)( struct subnet_record *, + struct userdata_struct *, + struct nmb_name *, + uint16_t, + int, + struct in_addr); +typedef void (*register_name_fail_function)( struct subnet_record *, + struct response_record *, + struct nmb_name *); + +typedef void (*release_name_success_function)( struct subnet_record *, + struct userdata_struct *, + struct nmb_name *, + struct in_addr); +typedef void (*release_name_fail_function)( struct subnet_record *, + struct response_record *, + struct nmb_name *); + +typedef void (*refresh_name_success_function)( struct subnet_record *, + struct userdata_struct *, + struct nmb_name *, + uint16_t, + int, + struct in_addr); +typedef void (*refresh_name_fail_function)( struct subnet_record *, + struct response_record *, + struct nmb_name *); + +typedef void (*query_name_success_function)( struct subnet_record *, + struct userdata_struct *, + struct nmb_name *, + struct in_addr, + struct res_rec *answers); + +typedef void (*query_name_fail_function)( struct subnet_record *, + struct response_record *, + struct nmb_name *, + int); + +typedef void (*node_status_success_function)( struct subnet_record *, + struct userdata_struct *, + struct res_rec *, + struct in_addr); +typedef void (*node_status_fail_function)( struct subnet_record *, + struct response_record *); + +/* Initiated name queries are recorded in this list to track any responses. */ + +struct response_record { + struct response_record *next; + struct response_record *prev; + + uint16_t response_id; + + /* Callbacks for packets received or not. */ + response_function resp_fn; + timeout_response_function timeout_fn; + + /* Callbacks for the request succeeding or not. */ + success_function success_fn; + fail_function fail_fn; + + struct packet_struct *packet; + + struct userdata_struct *userdata; + + int num_msgs; + + time_t repeat_time; + time_t repeat_interval; + int repeat_count; + + /* Recursion protection. */ + bool in_expiration_processing; +}; + +/* A subnet structure. It contains a list of workgroups and netbios names. */ + +/* + B nodes will have their own, totally separate subnet record, with their + own netbios name set. These do NOT interact with other subnet records' + netbios names. +*/ + +enum subnet_type { + NORMAL_SUBNET = 0, /* Subnet listed in interfaces list. */ + UNICAST_SUBNET = 1, /* Subnet for unicast packets. */ + REMOTE_BROADCAST_SUBNET = 2, /* Subnet for remote broadcasts. */ + WINS_SERVER_SUBNET = 3 /* Only created if we are a WINS server. */ +}; + +struct subnet_record { + struct subnet_record *next; + struct subnet_record *prev; + + char *subnet_name; /* For Debug identification. */ + enum subnet_type type; /* To catagorize the subnet. */ + + struct work_record *workgrouplist; /* List of workgroups. */ + struct name_record *namelist; /* List of netbios names. */ + struct response_record *responselist; /* List of responses expected. */ + + bool namelist_changed; + bool work_changed; + + struct in_addr bcast_ip; + struct in_addr mask_ip; + struct in_addr myip; + int nmb_sock; /* socket to listen for unicast 137. */ + int nmb_bcast; /* socket to listen for broadcast 137. */ + int dgram_sock; /* socket to listen for unicast 138. */ + int dgram_bcast; /* socket to listen for broadcast 138. */ +}; + +/* Broadcast packet announcement intervals, in minutes. */ + +/* Attempt to add domain logon and domain master names. */ +#define CHECK_TIME_ADD_DOM_NAMES 5 + +/* Search for master browsers of workgroups samba knows about, + except default. */ +#define CHECK_TIME_MST_BROWSE 5 + +/* Request backup browser announcements from other servers. */ +#define CHECK_TIME_ANNOUNCE_BACKUP 15 + +/* Request host announcements from other servers: min and max of interval. */ +#define CHECK_TIME_MIN_HOST_ANNCE 3 +#define CHECK_TIME_MAX_HOST_ANNCE 12 + +/* Announce as master to WINS server and any Primary Domain Controllers. */ +#define CHECK_TIME_MST_ANNOUNCE 15 + +/* Time between syncs from domain master browser to local master browsers. */ +#define CHECK_TIME_DMB_TO_LMB_SYNC 15 + +/* Do all remote announcements this often. */ +#define REMOTE_ANNOUNCE_INTERVAL 180 + +/* what is the maximum period between name refreshes. Note that this only + affects non-permanent self names (in seconds) */ +#define MAX_REFRESH_TIME (60*20) + +/* The Extinction interval: 4 days, time a node will stay in released state */ +#define EXTINCTION_INTERVAL (4*24*60*60) + +/* The Extinction time-out: 1 day, time a node will stay in deleted state */ +#define EXTINCTION_TIMEOUT (24*60*60) + +/* Macro's to enumerate subnets either with or without + the UNICAST subnet. */ + +extern struct subnet_record *subnetlist; +extern struct subnet_record *unicast_subnet; +extern struct subnet_record *wins_server_subnet; +extern struct subnet_record *remote_broadcast_subnet; + +#define FIRST_SUBNET subnetlist +#define NEXT_SUBNET_EXCLUDING_UNICAST(x) ((x)->next) +#define NEXT_SUBNET_INCLUDING_UNICAST(x) (get_next_subnet_maybe_unicast((x))) + +/* wins replication record used between nmbd and wrepld */ +typedef struct _WINS_RECORD { + char name[17]; + char type; + int nb_flags; + int wins_flags; + uint64_t id; + int num_ips; + struct in_addr ip[25]; + struct in_addr wins_ip; +} WINS_RECORD; + #include "nmbd/nmbd_proto.h" #define NMBD_WAIT_INTERFACES_TIME_USEC (250 * 1000) diff --git a/source3/nmbd/nmbd_packets.c b/source3/nmbd/nmbd_packets.c index a1d8dee..1da3657 100644 --- a/source3/nmbd/nmbd_packets.c +++ b/source3/nmbd/nmbd_packets.c @@ -43,6 +43,7 @@ bool nmbd_init_packet_server(void) status = nb_packet_server_create( NULL, nmbd_event_context(), + global_nmbd_socket_dir(), lp_parm_int(-1, "nmbd", "unexpected_clients", 200), &packet_server); if (!NT_STATUS_IS_OK(status)) { diff --git a/source3/rpc_server/wkssvc/srv_wkssvc_nt.c b/source3/rpc_server/wkssvc/srv_wkssvc_nt.c index 0724dd0..ed16278 100644 --- a/source3/rpc_server/wkssvc/srv_wkssvc_nt.c +++ b/source3/rpc_server/wkssvc/srv_wkssvc_nt.c @@ -50,7 +50,7 @@ static int dom_user_cmp(const struct dom_usr *usr1, const struct dom_usr *usr2) /* Called from qsort to compare two domain users in a dom_usr_t array * for sorting by login time. Return >0 if usr1 login time was later * than usr2 login time, <0 if it was earlier */ - return (usr1->login_time - usr2->login_time); + return NUMERIC_CMP(usr1->login_time, usr2->login_time); } /******************************************************************* diff --git a/source3/script/tests/test_recycle.sh b/source3/script/tests/test_recycle.sh index 8c9291f..ba1d0a5 100755 --- a/source3/script/tests/test_recycle.sh +++ b/source3/script/tests/test_recycle.sh @@ -90,11 +90,16 @@ quit return 0 } +panic_count_0=$(grep -c PANIC $SMBD_TEST_LOG) testit "recycle" \ test_recycle || failed=$((failed + 1)) +panic_count_1=$(grep -c PANIC $SMBD_TEST_LOG) + +testit "check_panic" test $panic_count_0 -eq $panic_count_1 || failed=$(expr $failed + 1) + # # Cleanup. do_cleanup diff --git a/source3/script/tests/test_widelink_dfs_ci.sh b/source3/script/tests/test_widelink_dfs_ci.sh new file mode 100755 index 0000000..6ae5cf5 --- /dev/null +++ b/source3/script/tests/test_widelink_dfs_ci.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +# regression test for dfs access with wide links enabled on dfs share +# Ensure we still maintain case insensitivity. + +if [ $# -lt 7 ]; then + cat <<EOF +Usage: test_widelink_dfs_ci.sh SERVER SERVER_IP SHARE USERNAME PASSWORD PREFIX SMBCLIENT <smbclient arguments> +EOF + exit 1 +fi + +SERVER="$1" +SERVER_IP="$2" +SHARE="$3" +USERNAME="$4" +PASSWORD="$5" +PREFIX="$6" +SMBCLIENT="$7" +shift 7 +ADDARGS="$@" + +incdir=$(dirname "$0")"/../../../testprogs/blackbox" +. "$incdir/subunit.sh" +. "$incdir/common_test_fns.inc" + +failed=0 + +# Do not let deprecated option warnings muck this up +SAMBA_DEPRECATED_SUPPRESS=1 +export SAMBA_DEPRECATED_SUPPRESS + +# Test chdir'ing into a lowercase directory with upper case. +test_ci() +{ + tmpfile="$PREFIX/smbclient_ci_commands" + + cat >"$tmpfile" <<EOF +mkdir x +cd X +cd .. +rmdir x +quit +EOF + + cmd='CLI_FORCE_INTERACTIVE=yes $SMBCLIENT "$@" -U$USERNAME%$PASSWORD //$SERVER/msdfs-share-wl -I $SERVER_IP $ADDARGS < $tmpfile 2>&1' + eval echo "$cmd" + out=$(eval "$cmd") + ret=$? + rm -f "$tmpfile" + + if [ $ret != 0 ]; then + echo "$out" + echo "failed create x then chdir into X with error $ret" + return 1 + fi + + echo "$out" | grep 'NT_STATUS_' + ret="$?" + if [ "$ret" -eq 0 ]; then + echo "$out" + echo "Error create x then chdir into X" + return 1 + fi + return 0 +} + +testit "creating a directory x and chdir into it" \ + test_ci || + failed=$((failed + 1)) + +testok "$0" "$failed" diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 0901c24..0648797 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -1808,6 +1808,17 @@ plantestsuite("samba3.blackbox.smbclient-bug15435", smbclient3, configuration]) +plantestsuite("samba3.blackbox.widelink_dfs_ci", + "fileserver", + [os.path.join(samba3srcdir, "script/tests/test_widelink_dfs_ci.sh"), + "$SERVER", + "$SERVER_IP", + "msdfs-share-wl", + "$USERNAME", + "$PASSWORD", + "$PREFIX", + smbclient3]) + if have_cluster_support: t = "readdir-timestamp" diff --git a/source3/smbd/files.c b/source3/smbd/files.c index 6aad76a..42bb323 100644 --- a/source3/smbd/files.c +++ b/source3/smbd/files.c @@ -1312,6 +1312,24 @@ next: } if (fd == -1) { + /* + * vfs_widelink widelink_openat will update stat for fsp + * and return ELOOP for non-existing link, we can report + * the link here and let calling code decide what to do. + */ + if ((errno == ELOOP) && S_ISLNK(fsp->fsp_name->st.st_ex_mode)) { + status = create_open_symlink_err(mem_ctx, + dirfsp, + &rel_fname, + &symlink_err); + if (NT_STATUS_IS_OK(status)) { + status = NT_STATUS_STOPPED_ON_SYMLINK; + } else { + DBG_ERR("read_symlink_reparse failed: %s\n", + nt_errstr(status)); + } + goto fail; + } status = map_nt_error_from_unix(errno); DBG_DEBUG("SMB_VFS_OPENAT() failed: %s\n", strerror(errno)); diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 4928d1f..a954499 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -522,6 +522,11 @@ struct smbXsrv_connection { } smbtorture; bool signing_mandatory; + /* + * This is ConstrainedConnection in MS-SMB2, + * but with reversed value... + */ + bool got_authenticated_session; } smb2; }; diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index afb86ab..5b83d4d 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -494,6 +494,17 @@ static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *xconn, goto inval; } + if (!xconn->smb2.got_authenticated_session) { + D_INFO("Got SMB2_TRANSFORM header, " + "but not no authenticated session yet " + "client[%s] server[%s]\n", + tsocket_address_string( + xconn->remote_address, talloc_tos()), + tsocket_address_string( + xconn->local_address, talloc_tos())); + goto inval; + } + if (len < SMB2_TF_HDR_SIZE) { DEBUG(1, ("%d bytes left, expected at least %d\n", (int)len, SMB2_TF_HDR_SIZE)); diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index ac71e55..d3b21ea 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -271,6 +271,13 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session, x->global->signing_flags &= ~SMBXSRV_SIGNING_REQUIRED; /* we map anonymous to guest internally */ guest = true; + } else { + /* + * Remember we got one authenticated session on the connection + * in order to allow SMB3 decryption to happen + * (sadly even for future anonymous connections). + */ + xconn->smb2.got_authenticated_session = true; } if (guest && (x->global->encryption_flags & SMBXSRV_ENCRYPTION_REQUIRED)) { @@ -288,7 +295,10 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session, } x->global->signing_algo = xconn->smb2.server.sign_algo; x->global->encryption_cipher = xconn->smb2.server.cipher; - if (guest) { + if (*out_session_flags & SMB2_SESSION_FLAG_IS_GUEST) { + /* + * A fallback to guest can't do any encryption + */ x->global->encryption_cipher = SMB2_ENCRYPTION_NONE; } @@ -642,6 +652,12 @@ static NTSTATUS smbd_smb2_bind_auth_return(struct smbXsrv_session *session, return NT_STATUS_LOGON_FAILURE; } + /* + * Remember we got one authenticated session on the connection + * in order to allow SMB3 decryption to happen + */ + xconn->smb2.got_authenticated_session = true; + *out_session_id = session->global->session_wire_id; return NT_STATUS_OK; diff --git a/source3/smbd/smb2_tcon.c b/source3/smbd/smb2_tcon.c index b228036..20d8967 100644 --- a/source3/smbd/smb2_tcon.c +++ b/source3/smbd/smb2_tcon.c @@ -331,6 +331,10 @@ static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req, } } + if (guest_session) { + /* make sure we don't ask for optional encryption */ + encryption_desired = false; + } if (encryption_desired) { encryption_flags |= SMBXSRV_ENCRYPTION_DESIRED; } diff --git a/source3/utils/conn_tdb.c b/source3/utils/conn_tdb.c index 3724bd4..3f4ef00 100644 --- a/source3/utils/conn_tdb.c +++ b/source3/utils/conn_tdb.c @@ -27,6 +27,7 @@ #include "conn_tdb.h" #include "util_tdb.h" #include "lib/util/string_wrappers.h" +#include "../libcli/security/session.h" struct connections_forall_state { struct db_context *session_by_pid; @@ -44,7 +45,7 @@ struct connections_forall_session { uint16_t cipher; uint16_t dialect; uint16_t signing; - uint8_t signing_flags; + bool authenticated; }; static int collect_sessions_fn(struct smbXsrv_session_global0 *global, @@ -56,6 +57,7 @@ static int collect_sessions_fn(struct smbXsrv_session_global0 *global, uint32_t id = global->session_global_id; struct connections_forall_session sess; + enum security_user_level ul; if (global->auth_session_info == NULL) { sess.uid = -1; @@ -69,7 +71,12 @@ static int collect_sessions_fn(struct smbXsrv_session_global0 *global, sess.cipher = global->channels[0].encryption_cipher; sess.signing = global->channels[0].signing_algo; sess.dialect = global->connection_dialect; - sess.signing_flags = global->signing_flags; + ul = security_session_user_level(global->auth_session_info, NULL); + if (ul >= SECURITY_USER) { + sess.authenticated = true; + } else { + sess.authenticated = false; + } status = dbwrap_store(state->session_by_pid, make_tdb_data((void*)&id, sizeof(id)), @@ -134,6 +141,7 @@ static int traverse_tcon_fn(struct smbXsrv_tcon_global0 *global, data.dialect = sess.dialect; data.signing = sess.signing; data.signing_flags = global->signing_flags; + data.authenticated = sess.authenticated; state->count++; diff --git a/source3/utils/conn_tdb.h b/source3/utils/conn_tdb.h index 2a6e04e..23a5e21 100644 --- a/source3/utils/conn_tdb.h +++ b/source3/utils/conn_tdb.h @@ -36,6 +36,7 @@ struct connections_data { uint16_t dialect; uint8_t signing_flags; uint16_t signing; + bool authenticated; }; /* The following definitions come from lib/conn_tdb.c */ diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c index d95a209..43fa026 100644 --- a/source3/utils/net_ads.c +++ b/source3/utils/net_ads.c @@ -521,6 +521,11 @@ static int net_ads_info_json(ADS_STRUCT *ads) goto failure; } + ret = json_add_string (&jsobj, "Workgroup", ads->config.workgroup); + if (ret != 0) { + goto failure; + } + ret = json_add_string (&jsobj, "Realm", ads->config.realm); if (ret != 0) { goto failure; @@ -627,6 +632,7 @@ static int net_ads_info(struct net_context *c, int argc, const char **argv) d_printf(_("LDAP server: %s\n"), addr); d_printf(_("LDAP server name: %s\n"), ads->config.ldap_server_name); + d_printf(_("Workgroup: %s\n"), ads->config.workgroup); d_printf(_("Realm: %s\n"), ads->config.realm); d_printf(_("Bind Path: %s\n"), ads->config.bind_path); d_printf(_("LDAP port: %d\n"), ads->ldap.port); diff --git a/source3/utils/net_registry.c b/source3/utils/net_registry.c index 5d1314e..b47a8ff 100644 --- a/source3/utils/net_registry.c +++ b/source3/utils/net_registry.c @@ -1146,7 +1146,7 @@ static int registry_value_cmp( if (v1->type == v2->type) { return data_blob_cmp(&v1->data, &v2->data); } - return v1->type - v2->type; + return NUMERIC_CMP(v1->type, v2->type); } static WERROR precheck_create_val(struct precheck_ctx *ctx, diff --git a/source3/utils/sharesec.c b/source3/utils/sharesec.c index a6481e2..4175729 100644 --- a/source3/utils/sharesec.c +++ b/source3/utils/sharesec.c @@ -120,19 +120,19 @@ static int ace_compare(struct security_ace *ace1, struct security_ace *ace2) return 0; if (ace1->type != ace2->type) - return ace2->type - ace1->type; + return NUMERIC_CMP(ace2->type, ace1->type); if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) return dom_sid_compare(&ace1->trustee, &ace2->trustee); if (ace1->flags != ace2->flags) - return ace1->flags - ace2->flags; + return NUMERIC_CMP(ace1->flags, ace2->flags); if (ace1->access_mask != ace2->access_mask) - return ace1->access_mask - ace2->access_mask; + return NUMERIC_CMP(ace1->access_mask, ace2->access_mask); if (ace1->size != ace2->size) - return ace1->size - ace2->size; + return NUMERIC_CMP(ace1->size, ace2->size); return memcmp(ace1, ace2, sizeof(struct security_ace)); } diff --git a/source3/utils/smbcacls.c b/source3/utils/smbcacls.c index e0591ac..5df7158 100644 --- a/source3/utils/smbcacls.c +++ b/source3/utils/smbcacls.c @@ -510,22 +510,23 @@ static int ace_compare(struct security_ace *ace1, struct security_ace *ace2) return -1; if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) && (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE)) - return ace1 - ace2; - - if (ace1->type != ace2->type) - return ace2->type - ace1->type; + return NUMERIC_CMP(ace2->type, ace1->type); + if (ace1->type != ace2->type) { + /* note the reverse order */ + return NUMERIC_CMP(ace2->type, ace1->type); + } if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) return dom_sid_compare(&ace1->trustee, &ace2->trustee); if (ace1->flags != ace2->flags) - return ace1->flags - ace2->flags; + return NUMERIC_CMP(ace1->flags, ace2->flags); if (ace1->access_mask != ace2->access_mask) - return ace1->access_mask - ace2->access_mask; + return NUMERIC_CMP(ace1->access_mask, ace2->access_mask); if (ace1->size != ace2->size) - return ace1->size - ace2->size; + return NUMERIC_CMP(ace1->size, ace2->size); return memcmp(ace1, ace2, sizeof(struct security_ace)); } diff --git a/source3/utils/status.c b/source3/utils/status.c index 4102b41..02a5f6d 100644 --- a/source3/utils/status.c +++ b/source3/utils/status.c @@ -483,9 +483,33 @@ static int traverse_connections_stdout(struct traverse_state *state, char *server_id, const char *machine, const char *timestr, - const char *encryption, - const char *signing) + const char *encryption_cipher, + enum crypto_degree encryption_degree, + const char *signing_cipher, + enum crypto_degree signing_degree) { + fstring encryption; + fstring signing; + + if (encryption_degree == CRYPTO_DEGREE_FULL) { + fstr_sprintf(encryption, "%s", encryption_cipher); + } else if (encryption_degree == CRYPTO_DEGREE_ANONYMOUS) { + fstr_sprintf(encryption, "anonymous(%s)", encryption_cipher); + } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) { + fstr_sprintf(encryption, "partial(%s)", encryption_cipher); + } else { + fstr_sprintf(encryption, "-"); + } + if (signing_degree == CRYPTO_DEGREE_FULL) { + fstr_sprintf(signing, "%s", signing_cipher); + } else if (signing_degree == CRYPTO_DEGREE_ANONYMOUS) { + fstr_sprintf(signing, "anonymous(%s)", signing_cipher); + } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) { + fstr_sprintf(signing, "partial(%s)", signing_cipher); + } else { + fstr_sprintf(signing, "-"); + } + d_printf("%-12s %-7s %-13s %-32s %-12s %-12s\n", servicename, server_id, machine, timestr, encryption, signing); @@ -538,7 +562,9 @@ static int traverse_connections(const struct connections_data *crec, return -1; } - if (smbXsrv_is_encrypted(crec->encryption_flags)) { + if (smbXsrv_is_encrypted(crec->encryption_flags) || + smbXsrv_is_partially_encrypted(crec->encryption_flags)) + { switch (crec->cipher) { case SMB_ENCRYPTION_GSSAPI: encryption = "GSSAPI"; @@ -549,14 +575,31 @@ static int traverse_connections(const struct connections_data *crec, case SMB2_ENCRYPTION_AES128_GCM: encryption = "AES-128-GCM"; break; + case SMB2_ENCRYPTION_AES256_CCM: + encryption = "AES-256-CCM"; + break; + case SMB2_ENCRYPTION_AES256_GCM: + encryption = "AES-256-GCM"; + break; default: encryption = "???"; break; } - encryption_degree = CRYPTO_DEGREE_FULL; + if (smbXsrv_is_encrypted(crec->encryption_flags)) { + encryption_degree = CRYPTO_DEGREE_FULL; + } else if (smbXsrv_is_partially_encrypted(crec->encryption_flags)) { + encryption_degree = CRYPTO_DEGREE_PARTIAL; + } + if (encryption_degree != CRYPTO_DEGREE_NONE && + !crec->authenticated) + { + encryption_degree = CRYPTO_DEGREE_ANONYMOUS; + } } - if (smbXsrv_is_signed(crec->signing_flags)) { + if (smbXsrv_is_signed(crec->signing_flags) || + smbXsrv_is_partially_signed(crec->signing_flags)) + { switch (crec->signing) { case SMB2_SIGNING_MD5_SMB1: signing = "HMAC-MD5"; @@ -574,7 +617,16 @@ static int traverse_connections(const struct connections_data *crec, signing = "???"; break; } - signing_degree = CRYPTO_DEGREE_FULL; + if (smbXsrv_is_signed(crec->signing_flags)) { + signing_degree = CRYPTO_DEGREE_FULL; + } else if (smbXsrv_is_partially_signed(crec->signing_flags)) { + signing_degree = CRYPTO_DEGREE_PARTIAL; + } + if (signing_degree != CRYPTO_DEGREE_NONE && + !crec->authenticated) + { + signing_degree = CRYPTO_DEGREE_ANONYMOUS; + } } if (!state->json_output) { @@ -584,7 +636,9 @@ static int traverse_connections(const struct connections_data *crec, crec->machine, timestr, encryption, - signing); + encryption_degree, + signing, + signing_degree); } else { result = traverse_connections_json(state, crec, @@ -615,6 +669,8 @@ static int traverse_sessionid_stdout(struct traverse_state *state, if (encryption_degree == CRYPTO_DEGREE_FULL) { fstr_sprintf(encryption, "%s", encryption_cipher); + } else if (encryption_degree == CRYPTO_DEGREE_ANONYMOUS) { + fstr_sprintf(encryption, "anonymous(%s)", encryption_cipher); } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) { fstr_sprintf(encryption, "partial(%s)", encryption_cipher); } else { @@ -622,6 +678,8 @@ static int traverse_sessionid_stdout(struct traverse_state *state, } if (signing_degree == CRYPTO_DEGREE_FULL) { fstr_sprintf(signing, "%s", signing_cipher); + } else if (signing_degree == CRYPTO_DEGREE_ANONYMOUS) { + fstr_sprintf(signing, "anonymous(%s)", signing_cipher); } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) { fstr_sprintf(signing, "partial(%s)", signing_cipher); } else { @@ -756,6 +814,11 @@ static int traverse_sessionid(const char *key, struct sessionid *session, } else if (smbXsrv_is_partially_encrypted(session->encryption_flags)) { encryption_degree = CRYPTO_DEGREE_PARTIAL; } + if (encryption_degree != CRYPTO_DEGREE_NONE && + !session->authenticated) + { + encryption_degree = CRYPTO_DEGREE_ANONYMOUS; + } } if (smbXsrv_is_signed(session->signing_flags) || @@ -783,6 +846,11 @@ static int traverse_sessionid(const char *key, struct sessionid *session, } else if (smbXsrv_is_partially_signed(session->signing_flags)) { signing_degree = CRYPTO_DEGREE_PARTIAL; } + if (signing_degree != CRYPTO_DEGREE_NONE && + !session->authenticated) + { + signing_degree = CRYPTO_DEGREE_ANONYMOUS; + } } diff --git a/source3/utils/status.h b/source3/utils/status.h index c08aba4..6674f0d 100644 --- a/source3/utils/status.h +++ b/source3/utils/status.h @@ -38,6 +38,7 @@ struct traverse_state { enum crypto_degree { CRYPTO_DEGREE_NONE, CRYPTO_DEGREE_PARTIAL, + CRYPTO_DEGREE_ANONYMOUS, CRYPTO_DEGREE_FULL }; diff --git a/source3/utils/status_json.c b/source3/utils/status_json.c index ee24a3b..f558c91 100644 --- a/source3/utils/status_json.c +++ b/source3/utils/status_json.c @@ -258,6 +258,8 @@ static int add_crypto_to_json(struct json_object *parent_json, if (degree == CRYPTO_DEGREE_NONE) { degree_str = "none"; + } else if (degree == CRYPTO_DEGREE_ANONYMOUS) { + degree_str = "anonymous"; } else if (degree == CRYPTO_DEGREE_PARTIAL) { degree_str = "partial"; } else { diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c index 5c9fe07..b800282 100644 --- a/source3/winbindd/idmap_ad.c +++ b/source3/winbindd/idmap_ad.c @@ -320,7 +320,10 @@ static NTSTATUS idmap_ad_get_tldap_ctx(TALLOC_CTX *mem_ctx, struct tldap_context **pld) { struct netr_DsRGetDCNameInfo *dcinfo; - struct sockaddr_storage dcaddr; + struct sockaddr_storage dcaddr = { + .ss_family = AF_UNSPEC, + }; + struct sockaddr_storage *pdcaddr = NULL; struct cli_credentials *creds; struct loadparm_context *lp_ctx; struct tldap_context *ld; @@ -362,9 +365,13 @@ static NTSTATUS idmap_ad_get_tldap_ctx(TALLOC_CTX *mem_ctx, * create_local_private_krb5_conf_for_domain() can deal with * sitename==NULL */ + if (strequal(domname, lp_realm()) || strequal(domname, lp_workgroup())) + { + pdcaddr = &dcaddr; + } ok = create_local_private_krb5_conf_for_domain( - lp_realm(), lp_workgroup(), sitename, &dcaddr); + lp_realm(), lp_workgroup(), sitename, pdcaddr); TALLOC_FREE(sitename); if (!ok) { DBG_DEBUG("Could not create private krb5.conf\n"); diff --git a/source3/wscript_build b/source3/wscript_build index ff8de1e..3fcdb55 100644 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -901,6 +901,7 @@ bld.SAMBA3_SUBSYSTEM('LIBNMB', LIBTSOCKET LIBCLI_NETLOGON samba3util + smbconf addns lmhosts resolv diff --git a/source4/dns_server/dns_crypto.c b/source4/dns_server/dns_crypto.c index be79a4e..d30e971 100644 --- a/source4/dns_server/dns_crypto.c +++ b/source4/dns_server/dns_crypto.c @@ -27,6 +27,7 @@ #include "libcli/util/ntstatus.h" #include "auth/auth.h" #include "auth/gensec/gensec.h" +#include "lib/util/bytearray.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_DNS @@ -106,7 +107,7 @@ WERROR dns_verify_tsig(struct dns_server *dns, struct dns_server_tkey *tkey = NULL; struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx, struct dns_fake_tsig_rec); - + const char *algorithm = NULL; /* Find the first TSIG record in the additional records */ for (i=0; i < packet->arcount; i++) { @@ -145,7 +146,7 @@ WERROR dns_verify_tsig(struct dns_server *dns, tkey = dns_find_tkey(dns->tkeys, state->tsig->name); if (tkey == NULL) { - DBG_DEBUG("dns_find_tkey() => NOTAUTH / DNS_RCODE_BADKEY\n"); + DBG_DEBUG("dns_find_tkey() => REFUSED / DNS_RCODE_BADKEY\n"); /* * We must save the name for use in the TSIG error * response and have no choice here but to save the @@ -157,10 +158,20 @@ WERROR dns_verify_tsig(struct dns_server *dns, return WERR_NOT_ENOUGH_MEMORY; } state->tsig_error = DNS_RCODE_BADKEY; - return DNS_ERR(NOTAUTH); + return DNS_ERR(REFUSED); } DBG_DEBUG("dns_find_tkey() => found\n"); + algorithm = state->tsig->rdata.tsig_record.algorithm_name; + if (strcmp(algorithm, "gss-tsig") == 0) { + /* ok */ + } else if (strcmp(algorithm, "gss.microsoft.com") == 0) { + /* ok */ + } else { + state->tsig_error = DNS_RCODE_BADKEY; + return DNS_ERR(REFUSED); + } + /* * Remember the keyname that found an existing tkey, used * later to fetch the key with dns_find_tkey() when signing @@ -183,7 +194,7 @@ WERROR dns_verify_tsig(struct dns_server *dns, } check_rec->rr_class = DNS_QCLASS_ANY; check_rec->ttl = 0; - check_rec->algorithm_name = talloc_strdup(check_rec, tkey->algorithm); + check_rec->algorithm_name = talloc_strdup(check_rec, algorithm); if (check_rec->algorithm_name == NULL) { return WERR_NOT_ENOUGH_MEMORY; } @@ -239,7 +250,7 @@ WERROR dns_verify_tsig(struct dns_server *dns, dump_data_dbgc(DBGC_DNS, 8, buffer, buffer_len); DBG_NOTICE("Verifying tsig failed: %s\n", nt_errstr(status)); state->tsig_error = DNS_RCODE_BADSIG; - return DNS_ERR(NOTAUTH); + return DNS_ERR(REFUSED); } if (!NT_STATUS_IS_OK(status)) { @@ -271,11 +282,19 @@ static WERROR dns_tsig_compute_mac(TALLOC_CTX *mem_ctx, struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx, struct dns_fake_tsig_rec); size_t mac_size = 0; + bool gss_tsig; if (check_rec == NULL) { return WERR_NOT_ENOUGH_MEMORY; } + if (strcmp(tkey->algorithm, "gss-tsig") == 0) { + gss_tsig = true; + } else { + /* gss.microsoft.com */ + gss_tsig = false; + } + /* first build and verify check packet */ check_rec->name = talloc_strdup(check_rec, tkey->name); if (check_rec->name == NULL) { @@ -315,6 +334,9 @@ static WERROR dns_tsig_compute_mac(TALLOC_CTX *mem_ctx, } buffer_len = mac_size; + if (gss_tsig && mac_size > 0) { + buffer_len += 2; + } buffer_len += packet_blob.length; if (buffer_len < packet_blob.length) { @@ -335,11 +357,21 @@ static WERROR dns_tsig_compute_mac(TALLOC_CTX *mem_ctx, /* * RFC 2845 "4.2 TSIG on Answers", how to lay out the buffer * that we're going to sign: - * 1. MAC of request (if present) + * 1. if MAC of request is present + * - 16bit big endian length of MAC of request + * - MAC of request * 2. Outgoing packet * 3. TSIG record */ if (mac_size > 0) { + if (gss_tsig) { + /* + * only gss-tsig not with + * gss.microsoft.com + */ + PUSH_BE_U16(p, 0, mac_size); + p += 2; + } memcpy(p, state->tsig->rdata.tsig_record.mac, mac_size); p += mac_size; } @@ -372,6 +404,7 @@ WERROR dns_sign_tsig(struct dns_server *dns, .data = NULL, .length = 0 }; + const char *algorithm = "gss-tsig"; tsig = talloc_zero(mem_ctx, struct dns_res_rec); if (tsig == NULL) { @@ -392,6 +425,8 @@ WERROR dns_sign_tsig(struct dns_server *dns, if (!W_ERROR_IS_OK(werror)) { return werror; } + + algorithm = tkey->algorithm; } tsig->name = talloc_strdup(tsig, state->key_name); @@ -402,7 +437,7 @@ WERROR dns_sign_tsig(struct dns_server *dns, tsig->rr_type = DNS_QTYPE_TSIG; tsig->ttl = 0; tsig->length = UINT16_MAX; - tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, "gss-tsig"); + tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, algorithm); if (tsig->rdata.tsig_record.algorithm_name == NULL) { return WERR_NOT_ENOUGH_MEMORY; } diff --git a/source4/dns_server/dns_query.c b/source4/dns_server/dns_query.c index 181beda..1f46ee0 100644 --- a/source4/dns_server/dns_query.c +++ b/source4/dns_server/dns_query.c @@ -663,8 +663,17 @@ static NTSTATUS create_tkey(struct dns_server *dns, { NTSTATUS status; struct dns_server_tkey_store *store = dns->tkeys; - struct dns_server_tkey *k = talloc_zero(store, struct dns_server_tkey); + struct dns_server_tkey *k = NULL; + + if (strcmp(algorithm, "gss-tsig") == 0) { + /* ok */ + } else if (strcmp(algorithm, "gss.microsoft.com") == 0) { + /* ok */ + } else { + return NT_STATUS_ACCESS_DENIED; + } + k = talloc_zero(store, struct dns_server_tkey); if (k == NULL) { return NT_STATUS_NO_MEMORY; } @@ -790,12 +799,22 @@ static WERROR handle_tkey(struct dns_server *dns, { struct dns_res_rec *in_tkey = NULL; struct dns_res_rec *ret_tkey; - uint16_t i; - for (i = 0; i < in->arcount; i++) { + /* + * TKEY needs to we the last one in + * additional or answers + */ + if (in->arcount >= 1) { + uint16_t i = in->arcount - 1; if (in->additional[i].rr_type == DNS_QTYPE_TKEY) { in_tkey = &in->additional[i]; - break; + } + } else if (in->nscount >= 1) { + /* no lookup */ + } else if (in->ancount >= 1) { + uint16_t i = in->ancount - 1; + if (in->answers[i].rr_type == DNS_QTYPE_TKEY) { + in_tkey = &in->answers[i]; } } diff --git a/source4/dns_server/dns_update.c b/source4/dns_server/dns_update.c index 4d2ee0b..1285111 100644 --- a/source4/dns_server/dns_update.c +++ b/source4/dns_server/dns_update.c @@ -570,6 +570,8 @@ static WERROR handle_one_update(struct dns_server *dns, W_ERROR_NOT_OK_RETURN(werror); for (i = first; i < rcount; i++) { + struct dnsp_DnssrvRpcRecord orig_rec = recs[i]; + if (!dns_record_match(&recs[i], &recs[rcount])) { continue; } @@ -583,6 +585,15 @@ static WERROR handle_one_update(struct dns_server *dns, werror = dns_replace_records(dns, mem_ctx, dn, needs_add, recs, rcount); DBG_DEBUG("dns_replace_records(REPLACE): %s\n", win_errstr(werror)); + if (W_ERROR_EQUAL(werror, WERR_ACCESS_DENIED) && + !needs_add && + orig_rec.dwTtlSeconds == recs[i].dwTtlSeconds) + { + DBG_NOTICE("dns_replace_records(REPLACE): %s " + "=> skip no-op\n", + win_errstr(werror)); + werror = WERR_OK; + } W_ERROR_NOT_OK_RETURN(werror); return WERR_OK; diff --git a/source4/dns_server/dnsserver_common.c b/source4/dns_server/dnsserver_common.c index aba7f41..88aed2e 100644 --- a/source4/dns_server/dnsserver_common.c +++ b/source4/dns_server/dnsserver_common.c @@ -68,6 +68,8 @@ uint8_t werr_to_dns_err(WERROR werr) return DNS_RCODE_NOTZONE; } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) { return DNS_RCODE_BADKEY; + } else if (W_ERROR_EQUAL(WERR_ACCESS_DENIED, werr)) { + return DNS_RCODE_REFUSED; } DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr))); return DNS_RCODE_SERVFAIL; @@ -642,7 +644,7 @@ static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1, * The records are sorted with higher types first, * which puts tombstones (type 0) last. */ - return r2->wType - r1->wType; + return NUMERIC_CMP(r2->wType, r1->wType); } /* * Then we need to sort from the oldest to newest timestamp. @@ -650,7 +652,7 @@ static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1, * Note that dwTimeStamp == 0 (never expiring) records come first, * then the ones whose expiry is soonest. */ - return r1->dwTimeStamp - r2->dwTimeStamp; + return NUMERIC_CMP(r1->dwTimeStamp, r2->dwTimeStamp); } /* @@ -1408,7 +1410,7 @@ static int dns_common_sort_zones(struct ldb_message **m1, struct ldb_message **m /* If the string lengths are not equal just sort by length */ if (l1 != l2) { /* If m1 is the larger zone name, return it first */ - return l2 - l1; + return NUMERIC_CMP(l2, l1); } /*TODO: We need to compare DNs here, we want the DomainDNSZones first */ diff --git a/source4/dsdb/repl/drepl_out_helpers.c b/source4/dsdb/repl/drepl_out_helpers.c index d46b19e..7ba5c79 100644 --- a/source4/dsdb/repl/drepl_out_helpers.c +++ b/source4/dsdb/repl/drepl_out_helpers.c @@ -1043,7 +1043,7 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req if (W_ERROR_EQUAL(status, WERR_DS_DRA_SCHEMA_MISMATCH)) { struct dreplsrv_partition *p; - bool ok; + struct tevent_req *subreq = NULL; if (was_schema) { nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP); @@ -1141,12 +1141,15 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req state->retry_started = true; - ok = dreplsrv_op_pull_source_detect_schema_cycle(req); - if (!ok) { + subreq = dreplsrv_out_drsuapi_send(state, + state->ev, + state->op->source_dsa->conn); + if (tevent_req_nomem(subreq, req)) { return; } - - dreplsrv_op_pull_source_get_changes_trigger(req); + tevent_req_set_callback(subreq, + dreplsrv_op_pull_source_connect_done, + req); return; } else if (!W_ERROR_IS_OK(status)) { @@ -1205,10 +1208,21 @@ static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req * operation once we are done. */ if (state->source_dsa_retry != NULL) { + struct tevent_req *subreq = NULL; + state->op->source_dsa = state->source_dsa_retry; state->op->extended_op = state->extended_op_retry; state->source_dsa_retry = NULL; - dreplsrv_op_pull_source_get_changes_trigger(req); + + subreq = dreplsrv_out_drsuapi_send(state, + state->ev, + state->op->source_dsa->conn); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + dreplsrv_op_pull_source_connect_done, + req); return; } diff --git a/source4/dsdb/samdb/ldb_modules/operational.c b/source4/dsdb/samdb/ldb_modules/operational.c index 1317b58..20613a7 100644 --- a/source4/dsdb/samdb/ldb_modules/operational.c +++ b/source4/dsdb/samdb/ldb_modules/operational.c @@ -1070,7 +1070,7 @@ static int pso_compare(struct ldb_message **m1, struct ldb_message **m2) return ndr_guid_compare(&guid1, &guid2); } else { - return prec1 - prec2; + return NUMERIC_CMP(prec1, prec2); } } diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 7aec006..2790679 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -1063,16 +1063,21 @@ static int replmd_ldb_message_element_attid_sort(const struct ldb_message_elemen a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name); /* - * TODO: remove this check, we should rely on e1 and e2 having valid attribute names - * in the schema + * If the elements do not have valid attribute names in the schema + * (which we would prefer to think can't happen), we need to sort them + * somehow. The current strategy is to put them at the end, sorted by + * attribute name. */ - if (!a1 || !a2) { + if (a1 == NULL && a2 == NULL) { return strcasecmp(e1->name, e2->name); } - if (a1->attributeID_id == a2->attributeID_id) { - return 0; + if (a1 == NULL) { + return 1; + } + if (a2 == NULL) { + return -1; } - return a1->attributeID_id > a2->attributeID_id ? 1 : -1; + return NUMERIC_CMP(a1->attributeID_id, a2->attributeID_id); } static void replmd_ldb_message_sort(struct ldb_message *msg, diff --git a/source4/dsdb/schema/schema_set.c b/source4/dsdb/schema/schema_set.c index 398091c..8b90e7f 100644 --- a/source4/dsdb/schema/schema_set.c +++ b/source4/dsdb/schema/schema_set.c @@ -478,19 +478,13 @@ static void dsdb_setup_attribute_shortcuts(struct ldb_context *ldb, struct dsdb_ TALLOC_FREE(frame); } -static int uint32_cmp(uint32_t c1, uint32_t c2) -{ - if (c1 == c2) return 0; - return c1 > c2 ? 1 : -1; -} - static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2) { return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName); } static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2) { - return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id); + return NUMERIC_CMP((*c1)->governsID_id, (*c2)->governsID_id); } static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2) { @@ -507,11 +501,11 @@ static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1, } static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2) { - return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id); + return NUMERIC_CMP((*a1)->attributeID_id, (*a2)->attributeID_id); } static int dsdb_compare_attribute_by_msDS_IntId(struct dsdb_attribute **a1, struct dsdb_attribute **a2) { - return uint32_cmp((*a1)->msDS_IntId, (*a2)->msDS_IntId); + return NUMERIC_CMP((*a1)->msDS_IntId, (*a2)->msDS_IntId); } static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2) { @@ -519,7 +513,7 @@ static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, } static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2) { - return uint32_cmp((*a1)->linkID, (*a2)->linkID); + return NUMERIC_CMP((*a1)->linkID, (*a2)->linkID); } static int dsdb_compare_attribute_by_cn(struct dsdb_attribute **a1, struct dsdb_attribute **a2) { diff --git a/source4/libcli/dgram/dgramsocket.c b/source4/libcli/dgram/dgramsocket.c index 154a667..2a98792 100644 --- a/source4/libcli/dgram/dgramsocket.c +++ b/source4/libcli/dgram/dgramsocket.c @@ -90,6 +90,10 @@ static void dgm_socket_recv(struct nbt_dgram_socket *dgmsock) dgmslot->handler(dgmslot, packet, src); } else { DEBUG(2,("No mailslot handler for '%s'\n", mailslot_name)); + /* dispatch if there is a general handler */ + if (dgmsock->incoming.handler) { + dgmsock->incoming.handler(dgmsock, packet, src); + } } } else { /* dispatch if there is a general handler */ @@ -205,6 +209,38 @@ NTSTATUS dgram_set_incoming_handler(struct nbt_dgram_socket *dgmsock, return NT_STATUS_OK; } +NTSTATUS nbt_dgram_send_raw(struct nbt_dgram_socket *dgmsock, + struct socket_address *dest, + const DATA_BLOB pkt_blob) +{ + struct nbt_dgram_request *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + req = talloc(dgmsock, struct nbt_dgram_request); + if (req == NULL) { + goto failed; + } + + req->dest = socket_address_copy(req, dest); + if (req->dest == NULL) { + goto failed; + } + + req->encoded = data_blob_dup_talloc(req, pkt_blob); + if (req->encoded.length != pkt_blob.length) { + goto failed; + } + + DLIST_ADD_END(dgmsock->send_queue, req); + + TEVENT_FD_WRITEABLE(dgmsock->fde); + + return NT_STATUS_OK; + +failed: + talloc_free(req); + return status; +} /* queue a datagram for send @@ -220,8 +256,8 @@ NTSTATUS nbt_dgram_send(struct nbt_dgram_socket *dgmsock, req = talloc(dgmsock, struct nbt_dgram_request); if (req == NULL) goto failed; - req->dest = dest; - if (talloc_reference(req, dest) == NULL) goto failed; + req->dest = socket_address_copy(req, dest); + if (req->dest == NULL) goto failed; ndr_err = ndr_push_struct_blob(&req->encoded, req, packet, (ndr_push_flags_fn_t)ndr_push_nbt_dgram_packet); diff --git a/source4/libcli/dgram/libdgram.h b/source4/libcli/dgram/libdgram.h index 0f313a6..7e57a94 100644 --- a/source4/libcli/dgram/libdgram.h +++ b/source4/libcli/dgram/libdgram.h @@ -83,6 +83,9 @@ struct dgram_mailslot_handler { /* prototypes */ +NTSTATUS nbt_dgram_send_raw(struct nbt_dgram_socket *dgmsock, + struct socket_address *dest, + const DATA_BLOB pkt_blob); NTSTATUS nbt_dgram_send(struct nbt_dgram_socket *dgmsock, struct nbt_dgram_packet *packet, struct socket_address *dest); diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c index e94512d..322a7bd 100644 --- a/source4/libcli/smb2/session.c +++ b/source4/libcli/smb2/session.c @@ -385,7 +385,9 @@ static void smb2_session_setup_spnego_both_ready(struct tevent_req *req) return; } - if (cli_credentials_is_anonymous(state->credentials)) { + if (cli_credentials_is_anonymous(state->credentials) && + !state->session->anonymous_session_key) + { /* * Windows server does not set the * SMB2_SESSION_FLAG_IS_GUEST nor @@ -399,10 +401,14 @@ static void smb2_session_setup_spnego_both_ready(struct tevent_req *req) return; } - status = gensec_session_key(session->gensec, state, - &session_key); - if (tevent_req_nterror(req, status)) { - return; + if (state->session->forced_session_key.length != 0) { + session_key = state->session->forced_session_key; + } else { + status = gensec_session_key(session->gensec, state, + &session_key); + if (tevent_req_nterror(req, status)) { + return; + } } if (state->session_bind) { diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h index 88e651a..1e2f185 100644 --- a/source4/libcli/smb2/smb2.h +++ b/source4/libcli/smb2/smb2.h @@ -128,6 +128,8 @@ struct smb2_session { struct gensec_security *gensec; struct smbXcli_session *smbXcli; bool needs_bind; + bool anonymous_session_key; + DATA_BLOB forced_session_key; }; diff --git a/source4/nbt_server/dgram/request.c b/source4/nbt_server/dgram/request.c index ea2b6e8..7614514 100644 --- a/source4/nbt_server/dgram/request.c +++ b/source4/nbt_server/dgram/request.c @@ -27,6 +27,11 @@ #include "nbt_server/dgram/proto.h" #include "librpc/gen_ndr/ndr_nbt.h" #include "param/param.h" +#include "lib/util/util_str_escape.h" +#include "lib/util/util_net.h" +#include "../source3/include/fstring.h" +#include "../source3/libsmb/nmblib.h" +#include "../source3/libsmb/unexpected.h" /* a list of mailslots that we have static handlers for @@ -51,8 +56,55 @@ void dgram_request_handler(struct nbt_dgram_socket *dgmsock, struct nbt_dgram_packet *packet, struct socket_address *src) { - DEBUG(0,("General datagram request from %s:%d\n", src->addr, src->port)); - NDR_PRINT_DEBUG(nbt_dgram_packet, packet); + struct nbtd_interface *iface = + talloc_get_type_abort(dgmsock->incoming.private_data, + struct nbtd_interface); + struct nbtd_server *nbtsrv = iface->nbtsrv; + const char *mailslot_name = NULL; + struct packet_struct *pstruct = NULL; + DATA_BLOB blob = { .length = 0, }; + enum ndr_err_code ndr_err; + + mailslot_name = dgram_mailslot_name(packet); + if (mailslot_name != NULL) { + DBG_DEBUG("Unexpected mailslot[%s] datagram request from %s:%d\n", + log_escape(packet, mailslot_name), + src->addr, src->port); + } else { + DBG_DEBUG("Unexpected general datagram request from %s:%d\n", + src->addr, src->port); + } + + if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) { + NDR_PRINT_DEBUG(nbt_dgram_packet, packet); + } + + /* + * For now we only pass DGRAM_DIRECT_UNIQUE + * messages via nb_packet_dispatch() to + * nbtsrv->unexpected_server + */ + if (packet->msg_type != DGRAM_DIRECT_UNIQUE) { + return; + } + + ndr_err = ndr_push_struct_blob(&blob, packet, packet, + (ndr_push_flags_fn_t)ndr_push_nbt_dgram_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DBG_ERR("ndr_push_nbt_dgram_packet - %s\n", + ndr_errstr(ndr_err)); + return; + } + + pstruct = parse_packet((char *)blob.data, + blob.length, + DGRAM_PACKET, + interpret_addr2(src->addr), + src->port); + if (pstruct != NULL) { + nb_packet_dispatch(nbtsrv->unexpected_server, pstruct); + free_packet(pstruct); + } } diff --git a/source4/nbt_server/interfaces.c b/source4/nbt_server/interfaces.c index b946a1d..0888c1b 100644 --- a/source4/nbt_server/interfaces.c +++ b/source4/nbt_server/interfaces.c @@ -31,6 +31,9 @@ #include "param/param.h" #include "lib/util/util_net.h" #include "lib/util/idtree.h" +#include "../source3/include/fstring.h" +#include "../source3/libsmb/nmblib.h" +#include "../source3/libsmb/unexpected.h" /* receive an incoming request and dispatch it to the right place @@ -115,7 +118,33 @@ static void nbtd_unexpected_handler(struct nbt_name_socket *nbtsock, } if (!req) { + struct packet_struct *pstruct = NULL; + DATA_BLOB blob = { .length = 0, }; + enum ndr_err_code ndr_err; + + /* + * Here we have NBT_FLAG_REPLY + */ DEBUG(10,("unexpected from src[%s] unable to redirected\n", src->addr)); + + ndr_err = ndr_push_struct_blob(&blob, packet, packet, + (ndr_push_flags_fn_t)ndr_push_nbt_name_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DBG_ERR("ndr_push_nbt_name_packet - %s\n", + ndr_errstr(ndr_err)); + return; + } + + pstruct = parse_packet((char *)blob.data, + blob.length, + NMB_PACKET, + interpret_addr2(src->addr), + src->port); + if (pstruct != NULL) { + nb_packet_dispatch(nbtsrv->unexpected_server, pstruct); + free_packet(pstruct); + } + return; } diff --git a/source4/nbt_server/nbt_server.c b/source4/nbt_server/nbt_server.c index 6d28bbd..c3f9fac 100644 --- a/source4/nbt_server/nbt_server.c +++ b/source4/nbt_server/nbt_server.c @@ -29,9 +29,113 @@ #include "auth/auth.h" #include "dsdb/samdb/samdb.h" #include "param/param.h" +#include "dynconfig/dynconfig.h" +#include "lib/util/pidfile.h" +#include "lib/util/util_net.h" +#include "lib/socket/socket.h" +#include "../source3/include/fstring.h" +#include "../source3/libsmb/nmblib.h" +#include "../source3/libsmb/unexpected.h" +#include "../source3/lib/util_procid.h" NTSTATUS server_service_nbtd_init(TALLOC_CTX *); +static void nbtd_server_msg_send_packet(struct imessaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id src, + size_t num_fds, + int *fds, + DATA_BLOB *data) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct nbtd_server *nbtsrv = + talloc_get_type_abort(private_data, + struct nbtd_server); + struct packet_struct *p = (struct packet_struct *)data->data; + struct sockaddr_storage ss; + struct socket_address *dst = NULL; + struct nbtd_interface *iface = NULL; + char buf[1024] = { 0, }; + DATA_BLOB blob = { .length = 0, }; + + DBG_DEBUG("Received send_packet from %u\n", (unsigned int)procid_to_pid(&src)); + + if (data->length != sizeof(struct packet_struct)) { + DBG_WARNING("Discarding invalid packet length from %u\n", + (unsigned int)procid_to_pid(&src)); + TALLOC_FREE(frame); + return; + } + + if ((p->packet_type != NMB_PACKET) && + (p->packet_type != DGRAM_PACKET)) { + DBG_WARNING("Discarding invalid packet type from %u: %d\n", + (unsigned int)procid_to_pid(&src), p->packet_type); + TALLOC_FREE(frame); + return; + } + + if (p->packet_type == DGRAM_PACKET) { + p->port = 138; + } + + in_addr_to_sockaddr_storage(&ss, p->ip); + dst = socket_address_from_sockaddr_storage(frame, &ss, p->port); + if (dst == NULL) { + TALLOC_FREE(frame); + return; + } + if (p->port == 0) { + DBG_WARNING("Discarding packet with missing port for addr[%s] " + "from %u\n", + dst->addr, (unsigned int)procid_to_pid(&src)); + TALLOC_FREE(frame); + return; + } + + iface = nbtd_find_request_iface(nbtsrv, dst->addr, true); + if (iface == NULL) { + DBG_WARNING("Could not find iface for packet to addr[%s] " + "from %u\n", + dst->addr, (unsigned int)procid_to_pid(&src)); + TALLOC_FREE(frame); + return; + } + + p->recv_fd = -1; + p->send_fd = -1; + + if (p->packet_type == DGRAM_PACKET) { + p->packet.dgram.header.source_ip.s_addr = interpret_addr(iface->ip_address); + p->packet.dgram.header.source_port = 138; + } + + blob.length = build_packet(buf, sizeof(buf), p); + if (blob.length == 0) { + TALLOC_FREE(frame); + return; + } + blob.data = (uint8_t *)buf; + + if (p->packet_type == DGRAM_PACKET) { + nbt_dgram_send_raw(iface->dgmsock, dst, blob); + } else { + nbt_name_send_raw(iface->nbtsock, dst, blob); + } + + TALLOC_FREE(frame); +} + +static int nbtd_server_destructor(struct nbtd_server *nbtsrv) +{ + struct task_server *task = nbtsrv->task; + + pidfile_unlink(lpcfg_pid_directory(task->lp_ctx), "nmbd"); + + return 0; +} + /* startup the nbtd task */ @@ -40,6 +144,8 @@ static NTSTATUS nbtd_task_init(struct task_server *task) struct nbtd_server *nbtsrv; NTSTATUS status; struct interface *ifaces; + const char *nmbd_socket_dir = NULL; + int unexpected_clients; load_interface_list(task, task->lp_ctx, &ifaces); @@ -66,6 +172,8 @@ static NTSTATUS nbtd_task_init(struct task_server *task) nbtsrv->bcast_interface = NULL; nbtsrv->wins_interface = NULL; + talloc_set_destructor(nbtsrv, nbtd_server_destructor); + /* start listening on the configured network interfaces */ status = nbtd_startup_interfaces(nbtsrv, task->lp_ctx, ifaces); if (!NT_STATUS_IS_OK(status)) { @@ -73,6 +181,30 @@ static NTSTATUS nbtd_task_init(struct task_server *task) return status; } + nmbd_socket_dir = lpcfg_parm_string(task->lp_ctx, + NULL, + "nmbd", + "socket dir"); + if (nmbd_socket_dir == NULL) { + nmbd_socket_dir = get_dyn_NMBDSOCKETDIR(); + } + + unexpected_clients = lpcfg_parm_int(task->lp_ctx, + NULL, + "nmbd", + "unexpected_clients", + 200); + + status = nb_packet_server_create(nbtsrv, + nbtsrv->task->event_ctx, + nmbd_socket_dir, + unexpected_clients, + &nbtsrv->unexpected_server); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "nbtd failed to start unexpected_server", true); + return status; + } + nbtsrv->sam_ctx = samdb_connect(nbtsrv, task->event_ctx, task->lp_ctx, @@ -93,11 +225,22 @@ static NTSTATUS nbtd_task_init(struct task_server *task) nbtd_register_irpc(nbtsrv); + status = imessaging_register(task->msg_ctx, + nbtsrv, + MSG_SEND_PACKET, + nbtd_server_msg_send_packet); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "nbtd failed imessaging_register(MSG_SEND_PACKET)", true); + return status; + } + /* start the process of registering our names on all interfaces */ nbtd_register_names(nbtsrv); irpc_add_name(task->msg_ctx, "nbt_server"); + pidfile_create(lpcfg_pid_directory(task->lp_ctx), "nmbd"); + return NT_STATUS_OK; } diff --git a/source4/nbt_server/nbt_server.h b/source4/nbt_server/nbt_server.h index c80e5bf..cbad3e9 100644 --- a/source4/nbt_server/nbt_server.h +++ b/source4/nbt_server/nbt_server.h @@ -78,6 +78,8 @@ struct nbtd_server { struct nbtd_statistics stats; struct ldb_context *sam_ctx; + + struct nb_packet_server *unexpected_server; }; diff --git a/source4/nbt_server/wins/winsdb.c b/source4/nbt_server/wins/winsdb.c index 2a05e96..7df40c3 100644 --- a/source4/nbt_server/wins/winsdb.c +++ b/source4/nbt_server/wins/winsdb.c @@ -32,6 +32,7 @@ #include "lib/socket/netif.h" #include "param/param.h" #include "lib/util/smb_strtox.h" +#include "lib/util/tsort.h" #undef strcasecmp @@ -349,7 +350,7 @@ static int winsdb_addr_sort_list (struct winsdb_addr **p1, struct winsdb_addr ** * then the replica addresses with the newest to the oldest address */ if (a2->expire_time != a1->expire_time) { - return a2->expire_time - a1->expire_time; + return NUMERIC_CMP(a2->expire_time, a1->expire_time); } if (strcmp(a2->wins_owner, h->local_owner) == 0) { @@ -360,7 +361,7 @@ static int winsdb_addr_sort_list (struct winsdb_addr **p1, struct winsdb_addr ** a1_owned = true; } - return a2_owned - a1_owned; + return NUMERIC_CMP(a2_owned, a1_owned); } struct winsdb_addr **winsdb_addr_list_add(struct winsdb_handle *h, const struct winsdb_record *rec, diff --git a/source4/nbt_server/wins/winsserver.c b/source4/nbt_server/wins/winsserver.c index a9f3ecd..6679961 100644 --- a/source4/nbt_server/wins/winsserver.c +++ b/source4/nbt_server/wins/winsserver.c @@ -36,6 +36,7 @@ #include "param/param.h" #include "libcli/resolve/resolve.h" #include "lib/util/util_net.h" +#include "lib/util/tsort.h" /* work out the ttl we will use given a client requested ttl @@ -653,7 +654,7 @@ static int nbtd_wins_randomize1Clist_sort(void *p1,/* (const char **) */ match_bits1 = ipv4_match_bits(interpret_addr2(a1), interpret_addr2(src->addr)); match_bits2 = ipv4_match_bits(interpret_addr2(a2), interpret_addr2(src->addr)); - return match_bits2 - match_bits1; + return NUMERIC_CMP(match_bits2, match_bits1); } static void nbtd_wins_randomize1Clist(struct loadparm_context *lp_ctx, diff --git a/source4/nbt_server/wscript_build b/source4/nbt_server/wscript_build index 9d0c24a..ce436e8 100644 --- a/source4/nbt_server/wscript_build +++ b/source4/nbt_server/wscript_build @@ -38,7 +38,7 @@ bld.SAMBA_SUBSYSTEM('NBTD_DGRAM', bld.SAMBA_SUBSYSTEM('NBT_SERVER', source='interfaces.c register.c query.c nodestatus.c defense.c packet.c irpc.c', autoproto='nbt_server_proto.h', - deps='cli-nbt NBTD_WINS NBTD_DGRAM service', + deps='cli-nbt NBTD_WINS NBTD_DGRAM service LIBNMB', enabled=bld.AD_DC_BUILD_IS_ENABLED() ) diff --git a/source4/ntvfs/posix/pvfs_streams.c b/source4/ntvfs/posix/pvfs_streams.c index 9210237..d2d5eed 100644 --- a/source4/ntvfs/posix/pvfs_streams.c +++ b/source4/ntvfs/posix/pvfs_streams.c @@ -22,6 +22,7 @@ #include "includes.h" #include "vfs_posix.h" #include "librpc/gen_ndr/xattr.h" +#include "lib/util/tsort.h" /* normalise a stream name, removing a :$DATA suffix if there is one @@ -51,7 +52,7 @@ static int stream_name_cmp(const char *name1, const char *name2) l1 = c1?(c1 - name1):strlen(name1); l2 = c2?(c2 - name2):strlen(name2); if (l1 != l2) { - return l1 - l2; + return NUMERIC_CMP(l1, l2); } ret = strncasecmp_m(name1, name2, l1); if (ret != 0) { diff --git a/source4/rpc_server/dnsserver/dnsdata.c b/source4/rpc_server/dnsserver/dnsdata.c index e6d35fc..6ffca19 100644 --- a/source4/rpc_server/dnsserver/dnsdata.c +++ b/source4/rpc_server/dnsserver/dnsdata.c @@ -1075,9 +1075,23 @@ int dns_name_compare(struct ldb_message * const *m1, struct ldb_message * const name1 = ldb_msg_find_attr_as_string(*m1, "name", NULL); name2 = ldb_msg_find_attr_as_string(*m2, "name", NULL); - if (name1 == NULL || name2 == NULL) { + /* + * We sort NULL names to the start of the list, because the only + * caller of this function, dnsserver_enumerate_records() will call + * dns_build_tree() with the sorted list, which will always return an + * error when it hits a NULL, so we might as well make that happen + * quickly. + */ + if (name1 == name2) { + /* this includes the both NULL case */ return 0; } + if (name1 == NULL) { + return -1; + } + if (name2 == NULL) { + return 1; + } /* Compare the last components of names. * If search_name is not NULL, compare the second last components of names */ diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c index 841c764..66a7785 100644 --- a/source4/rpc_server/samr/dcesrv_samr.c +++ b/source4/rpc_server/samr/dcesrv_samr.c @@ -1166,7 +1166,7 @@ static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call */ static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2) { - return e1->idx - e2->idx; + return NUMERIC_CMP(e1->idx, e2->idx); } static int compare_msgRid(struct ldb_message **m1, struct ldb_message **m2) { @@ -1197,8 +1197,9 @@ static int compare_msgRid(struct ldb_message **m1, struct ldb_message **m2) { } /* - * Get and compare the rids, if we fail to extract a rid treat it as a - * missing SID and sort to the end of the list + * Get and compare the rids. If we fail to extract a rid (because + * there are no subauths) the msg goes to the end of the list, but + * before the NULL SIDs. */ status = dom_sid_split_rid(NULL, sid1, NULL, &rid1); if (!NT_STATUS_IS_OK(status)) { diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index d70d7d5..e47eb57 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -531,7 +531,11 @@ plantestsuite_loadlist("samba.tests.dns_aging", "fl2003dc:local", plantestsuite_loadlist("samba.tests.dns_forwarder", "fl2003dc:local", [python, os.path.join(srcdir(), "python/samba/tests/dns_forwarder.py"), '$SERVER', '$SERVER_IP', '$DNS_FORWARDER1', '$DNS_FORWARDER2', '--machine-pass', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) -plantestsuite_loadlist("samba.tests.dns_tkey", "fl2008r2dc", [python, os.path.join(srcdir(), "python/samba/tests/dns_tkey.py"), '$SERVER', '$SERVER_IP', '--machine-pass', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) +plantestsuite_loadlist("samba.tests.dns_tkey", "fl2008r2dc", + ['USERNAME_UNPRIV=$DOMAIN_USER','PASSWORD_UNPRIV=$DOMAIN_USER_PASSWORD', + python, os.path.join(srcdir(), "python/samba/tests/dns_tkey.py"), + '$SERVER', '$SERVER_IP', '--machine-pass', '-U"$USERNAME%$PASSWORD"', + '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) plantestsuite_loadlist("samba.tests.dns_wildcard", "ad_dc", [python, os.path.join(srcdir(), "python/samba/tests/dns_wildcard.py"), '$SERVER', '$SERVER_IP', '--machine-pass', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) plantestsuite_loadlist("samba.tests.dns_invalid", "ad_dc", [python, os.path.join(srcdir(), "python/samba/tests/dns_invalid.py"), '$SERVER_IP', '--machine-pass', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) @@ -817,6 +821,11 @@ plantestsuite("samba4.blackbox.trust_ntlm", "fl2000dc:local", [os.path.join(bbdi plantestsuite("samba4.blackbox.trust_ntlm", "ad_member:local", [os.path.join(bbdir, "test_trust_ntlm.sh"), '$SERVER_IP', '$USERNAME', '$PASSWORD', '$SERVER', '$SERVER', '$DC_USERNAME', '$DC_PASSWORD', '$REALM', '$DOMAIN', 'member', 'auto', 'NT_STATUS_LOGON_FAILURE']) plantestsuite("samba4.blackbox.trust_ntlm", "nt4_member:local", [os.path.join(bbdir, "test_trust_ntlm.sh"), '$SERVER_IP', '$USERNAME', '$PASSWORD', '$SERVER', '$SERVER', '$DC_USERNAME', '$DC_PASSWORD', '$DOMAIN', '$DOMAIN', 'member', 'auto', 'NT_STATUS_LOGON_FAILURE']) +plantestsuite("samba4.blackbox.ldap_token", "fl2008r2dc:local", [os.path.join(bbdir, "test_ldap_token.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$REALM', '$DOMAIN', '$DOMSID']) +plantestsuite("samba4.blackbox.ldap_token", "fl2003dc:local", [os.path.join(bbdir, "test_ldap_token.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$REALM', '$DOMAIN', '$DOMSID']) +plantestsuite("samba4.blackbox.ldap_token", "fl2000dc:local", [os.path.join(bbdir, "test_ldap_token.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$REALM', '$DOMAIN', '$DOMSID']) +plantestsuite("samba4.blackbox.ldap_token", "ad_member:local", [os.path.join(bbdir, "test_ldap_token.sh"), '$DC_SERVER', '$DC_USERNAME', '$DC_PASSWORD', '$REALM', '$DOMAIN', '$DOMSID']) + plantestsuite("samba4.blackbox.trust_utils(fl2008r2dc:local)", "fl2008r2dc:local", [os.path.join(bbdir, "test_trust_utils.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$REALM', '$DOMAIN', '$TRUST_SERVER', '$TRUST_USERNAME', '$TRUST_PASSWORD', '$TRUST_REALM', '$TRUST_DOMAIN', '$PREFIX', "forest"]) plantestsuite("samba4.blackbox.trust_utils(fl2003dc:local)", "fl2003dc:local", [os.path.join(bbdir, "test_trust_utils.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$REALM', '$DOMAIN', '$TRUST_SERVER', '$TRUST_USERNAME', '$TRUST_PASSWORD', '$TRUST_REALM', '$TRUST_DOMAIN', '$PREFIX', "external"]) plantestsuite("samba4.blackbox.trust_utils(fl2000dc:local)", "fl2000dc:local", [os.path.join(bbdir, "test_trust_utils.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$REALM', '$DOMAIN', '$TRUST_SERVER', '$TRUST_USERNAME', '$TRUST_PASSWORD', '$TRUST_REALM', '$TRUST_DOMAIN', '$PREFIX', "external"]) @@ -1477,6 +1486,9 @@ planoldpythontestsuite("fileserver", "samba.tests.blackbox.smbcacls_dfs_propagate_inherit", "samba.tests.blackbox.smbcacls_dfs_propagate_inherit(DFS-msdfs-root)", environ={'SHARE': 'smbcacls_share'}) + +planoldpythontestsuite("fileserver", + "samba.tests.blackbox.misc_dfs_widelink") # # Want a selection of environments across the process models # diff --git a/source4/torture/smb2/ioctl.c b/source4/torture/smb2/ioctl.c index 3765dc0..beceaa5 100644 --- a/source4/torture/smb2/ioctl.c +++ b/source4/torture/smb2/ioctl.c @@ -7389,6 +7389,68 @@ static bool test_ioctl_bug14788_NETWORK_INTERFACE(struct torture_context *tortur } /* + * basic regression test for BUG 15664 + * https://bugzilla.samba.org/show_bug.cgi?id=15664 + */ +static bool test_ioctl_copy_chunk_bug15644(struct torture_context *torture, + struct smb2_tree *tree) +{ + struct smb2_handle dest_h; + NTSTATUS status; + union smb_ioctl ioctl; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct srv_copychunk chunk; + struct srv_copychunk_copy cc_copy; + enum ndr_err_code ndr_ret; + bool ok; + + ok = test_setup_create_fill(torture, + tree, + tmp_ctx, + FNAME2, + &dest_h, + 0, + SEC_RIGHTS_FILE_ALL, + FILE_ATTRIBUTE_NORMAL); + torture_assert(torture, ok, "dest file create fill"); + + ZERO_STRUCT(ioctl); + ioctl.smb2.level = RAW_IOCTL_SMB2; + ioctl.smb2.in.file.handle = dest_h; + ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK; + ioctl.smb2.in.max_output_response = sizeof(struct srv_copychunk_rsp); + ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + + ZERO_STRUCT(chunk); + ZERO_STRUCT(cc_copy); + /* overwrite the resume key with a bogus value */ + memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24); + cc_copy.chunk_count = 1; + cc_copy.chunks = &chunk; + cc_copy.chunks[0].source_off = 0; + cc_copy.chunks[0].target_off = 0; + cc_copy.chunks[0].length = 4096; + + ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, + &cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + torture_assert_ndr_success(torture, ndr_ret, + "ndr_push_srv_copychunk_copy"); + + /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */ + status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2); + torture_assert_ntstatus_equal(torture, status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + "FSCTL_SRV_COPYCHUNK"); + + status = smb2_util_close(tree, dest_h); + torture_assert_ntstatus_ok(torture, status, "close"); + + talloc_free(tmp_ctx); + return true; +} + +/* * testing of SMB2 ioctls */ struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx) @@ -7420,6 +7482,8 @@ struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx) test_ioctl_copy_chunk_dest_lck); torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key", test_ioctl_copy_chunk_bad_key); + torture_suite_add_1smb2_test(suite, "copy_chunk_bug15644", + test_ioctl_copy_chunk_bug15644); torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest", test_ioctl_copy_chunk_src_is_dest); torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap", diff --git a/source4/torture/smb2/session.c b/source4/torture/smb2/session.c index 823304f..2a3d0e6 100644 --- a/source4/torture/smb2/session.c +++ b/source4/torture/smb2/session.c @@ -5527,6 +5527,630 @@ static bool test_session_ntlmssp_bug14932(struct torture_context *tctx, struct s return ret; } +static bool test_session_anon_encryption1(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_tree *anon_tree = NULL; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + options.only_negprot = true; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + anon_creds, + &anon_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + anon_session = anon_tree->session; + transport = anon_session->transport; + + anon_session->anonymous_session_key = true; + smb2cli_session_torture_anonymous_encryption(anon_session->smbXcli, true); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + /* + * The connection is still in ConstrainedConnection state... + * + * This will use encryption and causes a connection reset + */ + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { + status = NT_STATUS_CONNECTION_RESET; + } + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_CONNECTION_RESET, + "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, !ok, "smbXcli_conn_is_connected still connected"); + + return true; +} + +static bool test_session_anon_encryption2(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *_creds = samba_cmdline_get_creds(); + struct cli_credentials *user_creds = NULL; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *user_session = NULL; + struct smb2_tree *user_tree = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_tree *anon_tree = NULL; + struct smb2_ioctl ioctl = { + .level = RAW_IOCTL_SMB2, + .in = { + .file = { + .handle = { + .data = { + [0] = UINT64_MAX, + [1] = UINT64_MAX, + }, + }, + }, + .function = FSCTL_QUERY_NETWORK_INTERFACE_INFO, + /* Windows client sets this to 64KiB */ + .max_output_response = 0x10000, + .flags = SMB2_IOCTL_FLAG_IS_FSCTL, + }, + }; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + uint32_t caps = smb2cli_conn_server_capabilities(transport0->conn); + NTSTATUS expected_mc_status; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + if (caps & SMB2_CAP_MULTI_CHANNEL) { + expected_mc_status = NT_STATUS_OK; + } else { + expected_mc_status = NT_STATUS_FS_DRIVER_REQUIRED; + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + user_creds = cli_credentials_shallow_copy(tctx, _creds); + torture_assert(tctx, user_creds != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(user_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + user_creds, + &user_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + user_session = user_tree->session; + transport = user_session->transport; + ok = smb2cli_tcon_is_encryption_on(user_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(user)"); + ok = smbXcli_session_is_authenticated(user_session->smbXcli); + torture_assert(tctx, ok, "smbXcli_session_is_authenticated(user)"); + + anon_session = smb2_session_init(transport, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx); + torture_assert(tctx, anon_session != NULL, "smb2_session_init(anon)"); + + anon_session->anonymous_session_key = true; + smb2cli_session_torture_anonymous_encryption(anon_session->smbXcli, true); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smb2cli_tcon_is_encryption_on(user_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(anon)"); + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + anon_tree = smb2_tree_init(anon_session, tctx, false); + torture_assert(tctx, anon_tree != NULL, "smb2_tree_init"); + + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_ok(tctx, status, + "smb2cli_tcon_recv(anon)"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + ok = smb2cli_tcon_is_encryption_on(anon_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(anon)"); + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + status = smb2_ioctl(user_tree, tctx, &ioctl); + torture_assert_ntstatus_equal(tctx, status, expected_mc_status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO user"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + status = smb2_ioctl(anon_tree, tctx, &ioctl); + torture_assert_ntstatus_equal(tctx, status, expected_mc_status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO anonymous"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + status = smb2_ioctl(user_tree, tctx, &ioctl); + torture_assert_ntstatus_equal(tctx, status, expected_mc_status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO user"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + status = smb2_ioctl(anon_tree, tctx, &ioctl); + torture_assert_ntstatus_equal(tctx, status, expected_mc_status, + "FSCTL_QUERY_NETWORK_INTERFACE_INFO anonymous"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + return true; +} + +static bool test_session_anon_encryption3(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *_creds = samba_cmdline_get_creds(); + struct cli_credentials *user_creds = NULL; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *user_session = NULL; + struct smb2_tree *user_tree = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_tree *anon_tree = NULL; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + uint8_t wrong_session_key[16] = { 0x1f, 0x2f, 0x3f, }; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + user_creds = cli_credentials_shallow_copy(tctx, _creds); + torture_assert(tctx, user_creds != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(user_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + user_creds, + &user_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + user_session = user_tree->session; + transport = user_session->transport; + ok = smb2cli_tcon_is_encryption_on(user_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(user)"); + ok = smbXcli_session_is_authenticated(user_session->smbXcli); + torture_assert(tctx, ok, "smbXcli_session_is_authenticated(user)"); + + anon_session = smb2_session_init(transport, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx); + torture_assert(tctx, anon_session != NULL, "smb2_session_init(anon)"); + + anon_session->anonymous_session_key = true; + anon_session->forced_session_key = data_blob_const(wrong_session_key, + ARRAY_SIZE(wrong_session_key)); + smb2cli_session_torture_anonymous_encryption(anon_session->smbXcli, true); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smb2cli_tcon_is_encryption_on(user_tree->smbXcli); + torture_assert(tctx, ok, "smb2cli_tcon_is_encryption_on(anon)"); + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + anon_tree = smb2_tree_init(anon_session, tctx, false); + torture_assert(tctx, anon_tree != NULL, "smb2_tree_init"); + + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { + status = NT_STATUS_CONNECTION_RESET; + } + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_CONNECTION_RESET, + "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, !ok, "smbXcli_conn_is_connected still connected"); + + return true; +} + +static bool test_session_anon_signing1(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_tree *anon_tree = NULL; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_signing(anon_creds, + SMB_SIGNING_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_signing"); + ok = cli_credentials_set_smb_ipc_signing(anon_creds, + SMB_SIGNING_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_ipc_signing"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_OFF, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + options.only_negprot = true; + options.signing = SMB_SIGNING_REQUIRED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + anon_creds, + &anon_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + anon_session = anon_tree->session; + transport = anon_session->transport; + + anon_session->anonymous_session_key = true; + smb2cli_session_torture_anonymous_signing(anon_session->smbXcli, true); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_ok(tctx, status, "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + return true; +} + +static bool test_session_anon_signing2(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = "IPC$"; + char *unc = NULL; + struct smb2_transport *transport0 = tree0->session->transport; + struct cli_credentials *anon_creds = NULL; + struct smbcli_options options; + struct smb2_transport *transport = NULL; + struct smb2_session *anon_session = NULL; + struct smb2_session *anon_session_nosign = NULL; + struct smb2_tree *anon_tree = NULL; + NTSTATUS status; + bool ok = true; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + uint8_t wrong_session_key[16] = { 0x1f, 0x2f, 0x3f, }; + uint64_t session_id; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + anon_creds = cli_credentials_init_anon(tctx); + torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon"); + ok = cli_credentials_set_smb_signing(anon_creds, + SMB_SIGNING_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_signing"); + ok = cli_credentials_set_smb_ipc_signing(anon_creds, + SMB_SIGNING_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_ipc_signing"); + ok = cli_credentials_set_smb_encryption(anon_creds, + SMB_ENCRYPTION_OFF, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options = transport0->options; + options.client_guid = GUID_random(); + options.only_negprot = true; + options.signing = SMB_SIGNING_REQUIRED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + anon_creds, + &anon_tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok(tctx, status, "smb2_connect failed"); + anon_session = anon_tree->session; + transport = anon_session->transport; + + anon_session->anonymous_session_key = true; + anon_session->forced_session_key = data_blob_const(wrong_session_key, + ARRAY_SIZE(wrong_session_key)); + smb2cli_session_torture_anonymous_signing(anon_session->smbXcli, true); + smb2cli_session_torture_no_signing_disconnect(anon_session->smbXcli); + + status = smb2_session_setup_spnego(anon_session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok(tctx, status, + "smb2_session_setup_spnego failed"); + + ok = smbXcli_session_is_authenticated(anon_session->smbXcli); + torture_assert(tctx, !ok, "smbXcli_session_is_authenticated(anon) wrong"); + + /* + * create a new structure for the same session id, + * but without smb2.should_sign set. + */ + session_id = smb2cli_session_current_id(anon_session->smbXcli); + anon_session_nosign = smb2_session_init(transport, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx); + torture_assert(tctx, anon_session_nosign != NULL, "smb2_session_init(anon_nosign)"); + smb2cli_session_set_id_and_flags(anon_session_nosign->smbXcli, session_id, 0); + smb2cli_session_torture_no_signing_disconnect(anon_session_nosign->smbXcli); + + timeout_msec = transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_ACCESS_DENIED, + "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + subreq = smb2cli_tcon_send(tctx, + tctx->ev, + transport->conn, + timeout_msec, + anon_session_nosign->smbXcli, + anon_tree->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_ok(tctx, status, "smb2cli_tcon_recv"); + + ok = smbXcli_conn_is_connected(transport->conn); + torture_assert(tctx, ok, "smbXcli_conn_is_connected"); + + return true; +} + struct torture_suite *torture_smb2_session_init(TALLOC_CTX *ctx) { struct torture_suite *suite = @@ -5599,6 +6223,11 @@ struct torture_suite *torture_smb2_session_init(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "encryption-aes-256-ccm", test_session_encryption_aes_256_ccm); torture_suite_add_1smb2_test(suite, "encryption-aes-256-gcm", test_session_encryption_aes_256_gcm); torture_suite_add_1smb2_test(suite, "ntlmssp_bug14932", test_session_ntlmssp_bug14932); + torture_suite_add_1smb2_test(suite, "anon-encryption1", test_session_anon_encryption1); + torture_suite_add_1smb2_test(suite, "anon-encryption2", test_session_anon_encryption2); + torture_suite_add_1smb2_test(suite, "anon-encryption3", test_session_anon_encryption3); + torture_suite_add_1smb2_test(suite, "anon-signing1", test_session_anon_signing1); + torture_suite_add_1smb2_test(suite, "anon-signing2", test_session_anon_signing2); suite->description = talloc_strdup(suite, "SMB2-SESSION tests"); diff --git a/testprogs/blackbox/test_ldap_token.sh b/testprogs/blackbox/test_ldap_token.sh new file mode 100755 index 0000000..5965590 --- /dev/null +++ b/testprogs/blackbox/test_ldap_token.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# Copyright (C) 2017 Stefan Metzmacher <metze@samba.org> + +if [ $# -lt 6 ]; then + cat <<EOF +Usage: $# test_ldap_token.sh SERVER USERNAME PASSWORD REALM DOMAIN DOMSID +EOF + exit 1 +fi + +SERVER=$1 +shift 1 +USERNAME=$1 +PASSWORD=$2 +REALM=$3 +DOMAIN=$4 +DOMSID=$5 +shift 5 +failed=0 + +. $(dirname $0)/subunit.sh +. $(dirname $0)/common_test_fns.inc + +ldbsearch=$(system_or_builddir_binary ldbsearch "${BINDIR}") + +test_token() +{ + auth_user="${1}" + shift 1 + auth_sid="${1}" + shift 1 + auth_args="$@" + + out=$($VALGRIND $ldbsearch -H ldap://$SERVER.$REALM ${auth_user} -b '' --scope=base ${auth_args} tokenGroups 2>&1) + ret=$? + test x"$ret" = x"0" || { + echo "$out" + return 1 + } + + domain_sids=$(echo "$out" | grep '^tokenGroups' | grep "${DOMSID}-" | wc -l) + test "$domain_sids" -ge "1" || { + echo "$out" + echo "Less than 1 sid from $DOMAIN $DOMSID" + return 1 + } + + builtin_sids=$(echo "$out" | grep '^tokenGroups' | grep "S-1-5-32-" | wc -l) + test "$builtin_sids" -ge "1" || { + echo "$out" + echo "Less than 1 sid from BUILTIN S-1-5-32" + return 1 + } + + # + # The following should always be present + # + # SID_WORLD(S-1-1-0) + # SID_NT_NETWORK(S-1-5-2) + # SID_NT_AUTHENTICATED_USERS(S-1-5-11) + # + required_sids="S-1-1-0 S-1-5-2 S-1-5-11 ${auth_sid}" + for sid in $required_sids; do + found=$(echo "$out" | grep "^tokenGroups: ${sid}$" | wc -l) + test x"$found" = x"1" || { + echo "$out" + echo "SID: ${sid} not found" + return 1 + } + done + + return 0 +} + +UARGS="-U$REALM\\$USERNAME%$PASSWORD" +# Check that SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY(S-1-18-1) is added for krb5 +AARGS="-k yes" +testit "Test token with kerberos USER (${AARGS})" test_token "${UARGS}" "S-1-18-1" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--use-kerberos=required" +testit "Test token with kerberos USER (${AARGS})" test_token "${UARGS}" "S-1-18-1" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--option=clientusekerberos=required" +testit "Test token with kerberos USER (${AARGS})" test_token "${UARGS}" "S-1-18-1" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--use-kerberos=required --option=clientusekerberos=off" +testit "Test token with kerberos USER (${AARGS})" test_token "${UARGS}" "S-1-18-1" "${AARGS}" || failed=$(expr $failed + 1) +# Check that SID_NT_NTLM_AUTHENTICATION(S-1-5-64-10) is added for NTLMSSP +AARGS="-k no" +testit "Test token with NTLMSSP USER (${AARGS})" test_token "${UARGS}" "S-1-5-64-10" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--use-kerberos=off" +testit "Test token with NTLMSSP USER (${AARGS})" test_token "${UARGS}" "S-1-5-64-10" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--option=clientusekerberos=off" +testit "Test token with NTLMSSP USER (${AARGS})" test_token "${UARGS}" "S-1-5-64-10" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--use-kerberos=off --option=clientusekerberos=required" +testit "Test token with NTLMSSP USER (${AARGS})" test_token "${UARGS}" "S-1-5-64-10" "${AARGS}" || failed=$(expr $failed + 1) + +UARGS="-P" +# Check that SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY(S-1-18-1) is added for krb5 +AARGS="-k yes" +testit "Test token with kerberos MACHINE (${AARGS})" test_token "${UARGS}" "S-1-18-1" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--use-kerberos=required" +testit "Test token with kerberos MACHINE (${AARGS})" test_token "${UARGS}" "S-1-18-1" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--option=clientusekerberos=required" +testit "Test token with kerberos MACHINE (${AARGS})" test_token "${UARGS}" "S-1-18-1" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--use-kerberos=required --option=clientusekerberos=off" +testit "Test token with kerberos MACHINE (${AARGS})" test_token "${UARGS}" "S-1-18-1" "${AARGS}" || failed=$(expr $failed + 1) +# Check that SID_NT_NTLM_AUTHENTICATION(S-1-5-64-10) is added for NTLMSSP +AARGS="-k no" +testit "Test token with NTLMSSP MACHINE (${AARGS})" test_token "${UARGS}" "S-1-5-64-10" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--use-kerberos=off" +testit "Test token with NTLMSSP MACHINE (${AARGS})" test_token "${UARGS}" "S-1-5-64-10" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--option=clientusekerberos=off" +testit "Test token with NTLMSSP MACHINE (${AARGS})" test_token "${UARGS}" "S-1-5-64-10" "${AARGS}" || failed=$(expr $failed + 1) +AARGS="--use-kerberos=off --option=clientusekerberos=required" +testit "Test token with NTLMSSP MACHINE (${AARGS})" test_token "${UARGS}" "S-1-5-64-10" "${AARGS}" || failed=$(expr $failed + 1) + +exit $failed diff --git a/testprogs/blackbox/test_trust_token.sh b/testprogs/blackbox/test_trust_token.sh index 075c032..92d4ad5 100755 --- a/testprogs/blackbox/test_trust_token.sh +++ b/testprogs/blackbox/test_trust_token.sh @@ -34,7 +34,7 @@ ldbsearch=$(system_or_builddir_binary ldbsearch "${BINDIR}") test_token() { auth_args="${1}" - auth_sid="${2-}" + auth_sid="${2}" out=$($VALGRIND $ldbsearch -H ldap://$SERVER.$REALM -U$TRUST_REALM\\$TRUST_USERNAME%$TRUST_PASSWORD -b '' --scope=base -k ${auth_args} tokenGroups 2>&1) ret=$? @@ -84,7 +84,8 @@ test_token() return 0 } -testit "Test token with kerberos" test_token "yes" "" || failed=$(expr $failed + 1) +# Check that SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY(S-1-18-1) is added for krb5 +testit "Test token with kerberos" test_token "yes" "S-1-18-1" || failed=$(expr $failed + 1) # Check that SID_NT_NTLM_AUTHENTICATION(S-1-5-64-10) is added for NTLMSSP testit "Test token with NTLMSSP" test_token "no" "S-1-5-64-10" || failed=$(expr $failed + 1) diff --git a/third_party/socket_wrapper/socket_wrapper.c b/third_party/socket_wrapper/socket_wrapper.c index c759d35..37799c8 100644 --- a/third_party/socket_wrapper/socket_wrapper.c +++ b/third_party/socket_wrapper/socket_wrapper.c @@ -1388,6 +1388,9 @@ static ssize_t libc_writev(int fd, const struct iovec *iov, int iovcnt) return swrap.libc.symbols._libc_writev.f(fd, iov, iovcnt); } +/* JEMALLOC: This tells socket_wrapper if it should handle syscall() */ +static bool swrap_handle_syscall; + #ifdef HAVE_SYSCALL DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE static long int libc_vsyscall(long int sysno, va_list va) @@ -1396,7 +1399,27 @@ static long int libc_vsyscall(long int sysno, va_list va) long int rc; int i; - swrap_bind_symbol_all(); + /* + * JEMALLOC: + * + * This is a workaround to prevent a deadlock in jemalloc calling + * malloc_init() twice. The first allocation call will trigger a + * malloc_init() of jemalloc. The functions calls syscall(SYS_open, ...) + * so it goes to socket or uid wrapper. In this code path we need to + * avoid any allocation calls. This will prevent the deadlock. + * + * We also need to avoid dlopen() as that would trigger the recursion + * into malloc_init(), so we use dlsym(RTLD_NEXT), until we reached + * swrap_constructor() or any real socket call at that time + * swrap_bind_symbol_all() will replace the function pointer again after + * dlopen of libc. + */ + if (swrap_handle_syscall) { + swrap_bind_symbol_all(); + } else if (swrap.libc.symbols._libc_syscall.obj == NULL) { + swrap.libc.symbols._libc_syscall.obj = dlsym(RTLD_NEXT, + "syscall"); + } for (i = 0; i < 8; i++) { args[i] = va_arg(va, long int); @@ -1517,6 +1540,8 @@ static void __swrap_bind_symbol_all_once(void) swrap_bind_symbol_rtld_default_optional(uid_wrapper_syscall_valid); swrap_bind_symbol_rtld_default_optional(uid_wrapper_syscall_va); #endif + + swrap_handle_syscall = true; } static void swrap_bind_symbol_all(void) @@ -8745,6 +8770,21 @@ long int syscall(long int sysno, ...) va_start(va, sysno); /* + * JEMALLOC: + * + * This is a workaround to prevent a deadlock in jemalloc calling + * malloc_init() twice. The first allocation call will trigger a + * malloc_init() of jemalloc. The functions calls syscall(SYS_open, ...) + * so it goes to socket or uid wrapper. In this code path we need to + * avoid any allocation calls. This will prevent the deadlock. + */ + if (!swrap_handle_syscall) { + rc = libc_vsyscall(sysno, va); + va_end(va); + return rc; + } + + /* * We should only handle the syscall numbers * we care about... */ @@ -8860,6 +8900,9 @@ void swrap_constructor(void) pthread_atfork(&swrap_thread_prepare, &swrap_thread_parent, &swrap_thread_child); + + /* Let socket_wrapper handle syscall() */ + swrap_handle_syscall = true; } /**************************** diff --git a/third_party/socket_wrapper/wscript b/third_party/socket_wrapper/wscript index 9d2210d..cdd3493 100644 --- a/third_party/socket_wrapper/wscript +++ b/third_party/socket_wrapper/wscript @@ -2,7 +2,7 @@ import os -VERSION = "1.4.2" +VERSION = "1.4.3" def configure(conf): @@ -10,6 +10,7 @@ def configure(conf): conf.DEFINE('USING_SYSTEM_SOCKET_WRAPPER', 1) libsocket_wrapper_so_path = 'libsocket_wrapper.so' else: + conf.CHECK_HEADERS('gnu/lib-names.h') if conf.CONFIG_SET("HAVE___THREAD"): conf.DEFINE("HAVE_GCC_THREAD_LOCAL_STORAGE", 1) diff --git a/third_party/uid_wrapper/uid_wrapper.c b/third_party/uid_wrapper/uid_wrapper.c index 5b6a82b..ca578e6 100644 --- a/third_party/uid_wrapper/uid_wrapper.c +++ b/third_party/uid_wrapper/uid_wrapper.c @@ -38,6 +38,10 @@ #include <pthread.h> +#ifdef HAVE_GNU_LIB_NAMES_H +#include <gnu/lib-names.h> +#endif + #ifdef HAVE_GCC_THREAD_LOCAL_STORAGE # define UWRAP_THREAD __thread #else @@ -558,6 +562,13 @@ static void *uwrap_load_lib_handle(enum uwrap_lib lib) switch (lib) { case UWRAP_LIBC: handle = uwrap.libc.handle; +#ifdef LIBC_SO + if (handle == NULL) { + handle = dlopen(LIBC_SO, flags); + + uwrap.libc.handle = handle; + } +#endif if (handle == NULL) { for (i = 10; i >= 0; i--) { char soname[256] = {0}; @@ -656,6 +667,9 @@ static void *_uwrap_bind_symbol(enum uwrap_lib lib, const char *fn_name) dlsym(RTLD_DEFAULT, #sym_name); \ } +/* JEMALLOC: This tells uid_wrapper if it should handle syscall() */ +static bool uwrap_handle_syscall; + /* DO NOT call this function during library initialization! */ static void __uwrap_bind_symbol_all_once(void) { @@ -699,6 +713,8 @@ static void __uwrap_bind_symbol_all_once(void) #endif uwrap_bind_symbol_libpthread(pthread_create); uwrap_bind_symbol_libpthread(pthread_exit); + + uwrap_handle_syscall = true; } static void uwrap_bind_symbol_all(void) @@ -863,7 +879,27 @@ static long int libc_vsyscall(long int sysno, va_list va) long int rc; int i; - uwrap_bind_symbol_all(); + /* + * JEMALLOC: + * + * This is a workaround to prevent a deadlock in jemalloc calling + * malloc_init() twice. The first allocation call will trigger a + * malloc_init() of jemalloc. The functions calls syscall(SYS_open, ...) + * so it goes to socket or uid wrapper. In this code path we need to + * avoid any allocation calls. This will prevent the deadlock. + * + * We also need to avoid dlopen() as that would trigger the recursion + * into malloc_init(), so we use dlsym(RTLD_NEXT), until we reached + * swrap_constructor() or any real socket call at that time + * swrap_bind_symbol_all() will replace the function pointer again after + * dlopen of libc. + */ + if (uwrap_handle_syscall) { + uwrap_bind_symbol_all(); + } else if (uwrap.libc.symbols._libc_syscall.obj == NULL) { + uwrap.libc.symbols._libc_syscall.obj = dlsym(RTLD_NEXT, + "syscall"); + } for (i = 0; i < 8; i++) { args[i] = va_arg(va, long int); @@ -1375,7 +1411,7 @@ static void uwrap_init_env(struct uwrap_thread *id) exit(-1); } - UWRAP_LOG(UWRAP_LOG_DEBUG, "Initalize groups with %s", env); + UWRAP_LOG(UWRAP_LOG_DEBUG, "Initialize groups with %s", env); id->ngroups = ngroups; } } @@ -2709,6 +2745,21 @@ long int syscall (long int sysno, ...) va_start(va, sysno); /* + * JEMALLOC: + * + * This is a workaround to prevent a deadlock in jemalloc calling + * malloc_init() twice. The first allocation call will trigger a + * malloc_init() of jemalloc. The functions calls syscall(SYS_open, ...) + * so it goes to socket or uid wrapper. In this code path we need to + * avoid any allocation calls. This will prevent the deadlock. + */ + if (!uwrap_handle_syscall) { + rc = libc_vsyscall(sysno, va); + va_end(va); + return rc; + } + + /* * We need to check for uwrap related syscall numbers before calling * uid_wrapper_enabled() otherwise we'd deadlock during the freebsd libc * fork() which calls syscall() after invoking uwrap_thread_prepare(). @@ -2821,6 +2872,9 @@ void uwrap_constructor(void) * for main process. */ uwrap_init(); + + /* Let socket_wrapper handle syscall() */ + uwrap_handle_syscall = true; } /**************************** diff --git a/third_party/uid_wrapper/wscript b/third_party/uid_wrapper/wscript index 7b65d93..5af7690 100644 --- a/third_party/uid_wrapper/wscript +++ b/third_party/uid_wrapper/wscript @@ -3,13 +3,15 @@ from waflib import Options import os, sys -VERSION="1.3.0" +VERSION="1.3.1" def configure(conf): if conf.CHECK_UID_WRAPPER(): conf.DEFINE('USING_SYSTEM_UID_WRAPPER', 1) libuid_wrapper_so_path = 'libuid_wrapper.so' else: + conf.CHECK_HEADERS('gnu/lib-names.h') + # check HAVE_GCC_ATOMIC_BUILTINS conf.CHECK_CODE(''' #include <stdbool.h> @@ -140,7 +140,27 @@ def options(opt): dest='with_smb1server', help=("Build smbd with SMB1 support (default=yes).")) + opt.add_option('--vendor-name', + help=('Specify a vendor (or packager) name to include in the version string'), + type="string", + dest='SAMBA_VERSION_VENDOR_SUFFIX', + default=None) + + opt.add_option('--vendor-patch-revision', + help=('Specify a vendor (or packager) patch revision number include in the version string (requires --vendor-name)'), + type="int", + dest='SAMBA_VERSION_VENDOR_PATCH', + default=None) + def configure(conf): + if Options.options.SAMBA_VERSION_VENDOR_SUFFIX: + conf.env.SAMBA_VERSION_VENDOR_SUFFIX = Options.options.SAMBA_VERSION_VENDOR_SUFFIX + + if Options.options.SAMBA_VERSION_VENDOR_PATCH: + if not Options.options.SAMBA_VERSION_VENDOR_SUFFIX: + raise conf.fatal('--vendor-patch-revision requires --vendor-version') + conf.env.SAMBA_VERSION_VENDOR_PATCH = Options.options.SAMBA_VERSION_VENDOR_PATCH + version = samba_version.load_version(env=conf.env) conf.DEFINE('CONFIG_H_IS_FROM_SAMBA', 1) |