The branch, v4-23-stable has been updated
       via  7a8bcd84200 VERSION: Disable GIT_SNAPSHOT for the 4.23.0rc3 release.
       via  2e5edf70589 WHATSNEW: Add release notes for Samba 4.23.0rc3.
       via  1fe870aa4c8 WHATSNEW: unify format
       via  cd46d6f2bd8 WHATSNEW: add Initial version of smb_prometheus_endpoint
       via  601e0e2c744 WHATSNEW: add Modern write time update logic
       via  1757f22046d WHATSNEW: add SMB3 Unix Extensions
       via  fb9a042dab9 WHATSNEW: add support for SMB3 over QUIC
       via  20c3ccc7be7 s4:lib/tls: add additional dns hostnames as 
GNUTLS_SAN_DNSNAME for self-signed certificates
       via  ef469b45168 s4:lib/tls: set GNUTLS_SAN_DNSNAME for self-signed 
certificates
       via  fbb1a8bfd8a s4:lib/tls: let tstream_tls_params_server_lpcfg() use 
lpcfg_dns_hostname() internally
       via  3710cb26ae3 auth:creds: Update the documentation for set_principal 
and set_realm
       via  1505f130450 auth:creds: Make sure to uppercase the realm of a 
principal
       via  e9b5835127f auth:creds: Validate realm names in set_realm and 
set_principal
       via  2629f19dbe5 s3:utils: Keep password secret in ntlm_auth 
get_password()
       via  ae5124ac5f5 auth:creds: Keep password secret in 
cmdline_get_userpassword()
       via  16b4aa95658 auth:creds: Keep the password secret
       via  8f98180ed71 auth:creds: Allow to reset the principal by passing 
NULL to set_principal
       via  e6158a6bf6d auth:creds: Also uppercase realm set via a callback
       via  3985c45ad97 auth:creds: Allow to reset the realm by passing NULL
       via  2788551866a smbd: return correct reparse tag DFS when listing 
directories
       via  607d7ad27c4 CI: add Python test 
samba.tests.dcerpc.dfs.DfsTests.test_dfs_reparse_tag
       via  24ba677d127 python/tests: also populate self.server in calls 
LibsmbTests setup()
       via  d0fa3266ad8 pylibsmb: add SMB2_FIND_ID_BOTH_DIRECTORY_INFO
       via  d22c428c1da vfs_xattr_tdb: fix dangling symlink detection
       via  8619973d978 s3/rpc_server/dfs: fix creating a DFS link
       via  6dc245c4669 VERSION: Bump version up to Samba 4.23.0rc3...
      from  4936cd33142 VERSION: Disable GIT_SNAPSHOT for the 4.23.0rc2 release.

https://git.samba.org/?p=samba.git;a=shortlog;h=v4-23-stable


- Log -----------------------------------------------------------------
-----------------------------------------------------------------------

Summary of changes:
 VERSION                                |   2 +-
 WHATSNEW.txt                           |  91 ++++++++++++++++-----
 auth/credentials/credentials.c         | 140 ++++++++++++++++++++++++++++-----
 auth/credentials/credentials_cmdline.c |   1 +
 python/samba/tests/credentials.py      |   4 +-
 python/samba/tests/dcerpc/dfs.py       |  48 +++++++++++
 python/samba/tests/libsmb.py           |   1 +
 source3/libsmb/pylibsmb.c              |   1 +
 source3/modules/vfs_xattr_tdb.c        |  13 ++-
 source3/rpc_server/dfs/srv_dfs_nt.c    |   4 +-
 source3/smbd/dir.c                     |   2 +
 source3/smbd/server.c                  |   8 --
 source3/utils/ntlm_auth.c              |   1 +
 source4/ldap_server/ldap_server.c      |  10 ---
 source4/ldap_server/ldap_server.h      |   1 -
 source4/lib/tls/tls.h                  |   3 +-
 source4/lib/tls/tls_tstream.c          |   6 +-
 source4/lib/tls/tlscert.c              |  16 ++++
 source4/selftest/tests.py              |   1 +
 19 files changed, 279 insertions(+), 74 deletions(-)
 create mode 100644 python/samba/tests/dcerpc/dfs.py


Changeset truncated at 500 lines:

diff --git a/VERSION b/VERSION
index b91b719c4b5..35ac38d9d15 100644
--- a/VERSION
+++ b/VERSION
@@ -89,7 +89,7 @@ SAMBA_VERSION_PRE_RELEASE=
 # e.g. SAMBA_VERSION_RC_RELEASE=1                      #
 #  ->  "3.0.0rc1"                                      #
 ########################################################
-SAMBA_VERSION_RC_RELEASE=2
+SAMBA_VERSION_RC_RELEASE=3
 
 ########################################################
 # To mark SVN snapshots this should be set to 'yes'    #
diff --git a/WHATSNEW.txt b/WHATSNEW.txt
index 051fa2084de..7367f771995 100644
--- a/WHATSNEW.txt
+++ b/WHATSNEW.txt
@@ -1,7 +1,7 @@
 Release Announcements
 =====================
 
-This is the second release candidate release of Samba 4.23.  This is *not*
+This is the third release candidate release of Samba 4.23.  This is *not*
 intended for production environments and is designed for testing
 purposes only.  Please report any defects via the Samba bug reporting
 system at https://bugzilla.samba.org/.
@@ -18,23 +18,60 @@ NEW FEATURES/CHANGES
 
 Enable SMB3 Unix Extensions by default
 --------------------------------------
-todo
+Starting with Samba 4.23, the SMB3 UNIX Extensions are enabled by
+default. These extensions provide first-class support for POSIX semantics
+over SMB3, allowing UNIX and Linux clients to access file services with
+features such as proper POSIX permissions, symlink handling, hardlinks,
+and special file types.
+
+Enabling this feature by default improves interoperability for UNIX/Linux
+clients without requiring additional configuration. Windows clients that
+do not support the extensions will continue to function normally, by
+using standard SMB3 behavior.
 
 Add support for SMB3 over QUIC
 ------------------------------
-todo
+The new "client smb transports" and "server smb transport"
+allow a more flexible configuration for the used tcp
+sockets.
+
+It also got the ability specify "quic" as possible transport.
+If quic should be used in addition to the defaults something
+like "server smb transports = +quic" can be used.
+
+For the client quic only works with name based uncs,
+ip address based uncs are not supported.
+
+Note for the server 'quic' requires the quic.ko kernel module
+for Linux from https://github.com/lxin/quic (tested with Linux 6.14).
+Future Linux versions may support it natively, here's the
+branch that will hopefully accepted upstream soon:
+https://github.com/lxin/net-next/commits/quic/
+
+For the client side there's a fallback to the userspace ngtcp2
+library if the quic kernel module is not available.
+
+Check the smb.conf manpage for additional hints
+about the "client smb transports" and "server smb transport"
+options and interactions with tls related options.
 
 Modern write time update logic
 ------------------------------
-todo
+Samba 4.23 changes file timestamp handling to match modern Windows servers.
+Earlier releases used delayed write time updates, where last_write_time was
+only refreshed after a short idle period. Now Samba applies immediate
+timestamp updates consistent with modern Windows 10/Server 2016 or newer.
 
 Initial version of smb_prometheus_endpoint
 ------------------------------------------
-todo
+Samba 4.23 introduces the smb_prometheus_endpoint utility, which exports
+Samba server metrics in Prometheus-compatible format. This enables seamless
+integration of Samba performance and status monitoring into existing
+Prometheus and Grafana environments. For usage and configuration details,
+refer to the new smb_prometheus_endpoint man page.
 
 samba-tool domain backup --no-secrets avoids confidential attributes
 --------------------------------------------------------------------
-
 The --no-secrets option creates a back-up without secret attributes
 (e.g. passwords), suitable for use in a lab domain. Until now it could
 still contain confidential attributes, including BitLocker recovery
@@ -45,20 +82,19 @@ schema to have confidential attributes and are no use 
without them.
 
 CTDB changes
 ------------
-
-* CTDB now supports loading tunables from
-  /etc/ctdb/tunables.d/*.tunables, in addition to the standard
-  /etc/ctdb/tunables.conf.  See the ctdb-tunables(7) manual page for
-  more details.  Note that the above locations are examples - the
-  actual location of these files will depend on compile time
-  configuration.
-
-  It isn't expected that many users will require a directory of tunables
-  files, since most users do not need to change tunables from their
-  default values.  However, this allows vendors to ship their required
-  tunables settings (for example, in one or more files marked "do not
-  edit") while still allowing local administrators to add their own
-  tunables settings (in one or more separate files).
+CTDB now supports loading tunables from
+/etc/ctdb/tunables.d/*.tunables, in addition to the standard
+/etc/ctdb/tunables.conf.  See the ctdb-tunables(7) manual page for
+more details.  Note that the above locations are examples - the
+actual location of these files will depend on compile time
+configuration.
+
+It isn't expected that many users will require a directory of tunables
+files, since most users do not need to change tunables from their
+default values.  However, this allows vendors to ship their required
+tunables settings (for example, in one or more files marked "do not
+edit") while still allowing local administrators to add their own
+tunables settings (in one or more separate files).
 
 Per-share profiling stats
 -------------------------
@@ -89,6 +125,21 @@ smb.conf changes
   server smb transports                   New             tcp, nbt
   winbind varlink service                 New             no
 
+
+CHANGES SINCE 4.23.0rc2
+=======================
+
+o  Ralph Boehme <[email protected]>
+   * BUG 15843: macOS Finder client DFS broken on 4.22.0.
+
+o  Stefan Metzmacher <[email protected]>
+   * BUG 15899: Self-signed certificates don't have X509v3 Subject Alternative
+     Name for DNS.
+
+o  Andreas Schneider <[email protected]>
+   * BUG 15893: Improve handling of principals and realms in client tools.
+
+
 CHANGES SINCE 4.23.0rc1
 =======================
 
diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
index c31470a81d2..dab1c047c13 100644
--- a/auth/credentials/credentials.c
+++ b/auth/credentials/credentials.c
@@ -33,6 +33,18 @@
 #include "system/filesys.h"
 #include "system/passwd.h"
 
+static bool str_is_ascii(const char *s) {
+       if (s != NULL) {
+               for (; s[0] != '\0'; s++) {
+                       if (!isascii(s[0])) {
+                               return false;
+                       }
+               }
+       }
+
+       return true;
+}
+
 /**
  * Create a new credentials structure
  * @param mem_ctx TALLOC_CTX parent for credentials structure
@@ -367,9 +379,31 @@ _PUBLIC_ char 
*cli_credentials_get_principal_and_obtained(struct cli_credentials
 
        if (cred->principal_obtained == CRED_CALLBACK &&
            !cred->callback_running) {
+               const char *princ = NULL;
+
                cred->callback_running = true;
-               cred->principal = cred->principal_cb(cred);
+               princ = cred->principal_cb(cred);
                cred->callback_running = false;
+
+               cred->principal = NULL;
+               if (princ != NULL) {
+                       char *p = NULL;
+
+                       cred->principal = talloc_strdup(cred, princ);
+                       if (cred->principal == NULL) {
+                               return NULL;
+                       }
+
+                       p = strchr(cred->principal, '@');
+                       if (p != NULL) {
+                               p += 1;
+
+                               for (; p[0] != '\0'; p++) {
+                                       *p = toupper(p[0]);
+                               }
+                       }
+               }
+
                if (cred->principal_obtained == CRED_CALLBACK) {
                        cred->principal_obtained = CRED_CALLBACK_RESULT;
                        cli_credentials_invalidate_ccache(cred, 
cred->principal_obtained);
@@ -427,17 +461,52 @@ _PUBLIC_ char *cli_credentials_get_principal(struct 
cli_credentials *cred, TALLO
        return cli_credentials_get_principal_and_obtained(cred, mem_ctx, 
&obtained);
 }
 
+/**
+ * @brief Set the principal for the credentials context.
+ *
+ * The realm of the principal will be checked if it is ASCII only and upper
+ * cased if it isn't yet.
+ *
+ * @param cred The credential context.
+ *
+ * @param val  The principal to set or NULL to reset.
+ *
+ * @param obtained            This way the described principal was specified.
+ *
+ * @return true on success, false if the realm is not ASCII or the allocation
+ * failed.
+ */
 _PUBLIC_ bool cli_credentials_set_principal(struct cli_credentials *cred,
-                                  const char *val,
-                                  enum credentials_obtained obtained)
+                                           const char *val,
+                                           enum credentials_obtained obtained)
 {
        if (obtained >= cred->principal_obtained) {
-               cred->principal = talloc_strdup(cred, val);
-               if (cred->principal == NULL) {
-                       return false;
+               /* If `val = NULL` is passed, principal is reset */
+               cred->principal = NULL;
+               if (val != NULL) {
+                       char *p = strchr(val, '@');
+                       if (p != NULL) {
+                               /* For realm names, only ASCII is allowed */
+                               if (!str_is_ascii(p + 1)) {
+                                       return false;
+                               }
+                       }
+
+                       cred->principal = talloc_strdup(cred, val);
+                       if (cred->principal == NULL) {
+                               return false;
+                       }
+
+                       p = strchr(cred->principal, '@');
+                       if (p != NULL) {
+                               p += 1;
+
+                               for (; p[0] != '\0'; p++) {
+                                       *p = toupper(p[0]);
+                               }
+                       }
                }
                cred->principal_obtained = obtained;
-
                cli_credentials_invalidate_ccache(cred, 
cred->principal_obtained);
                return true;
        }
@@ -623,6 +692,7 @@ _PUBLIC_ bool cli_credentials_set_password(struct 
cli_credentials *cred,
                if (cred->password == NULL) {
                        return false;
                }
+               talloc_keep_secret(discard_const(cred->password));
 
                /* Don't print the actual password in talloc memory dumps */
                talloc_set_name_const(cred->password,
@@ -912,9 +982,20 @@ _PUBLIC_ const char *cli_credentials_get_realm(struct 
cli_credentials *cred)
 
        if (cred->realm_obtained == CRED_CALLBACK &&
            !cred->callback_running) {
+               const char *realm = NULL;
+
                cred->callback_running = true;
-               cred->realm = cred->realm_cb(cred);
+               realm = cred->realm_cb(cred);
                cred->callback_running = false;
+
+               cred->realm = NULL;
+               if (realm != NULL) {
+                       cred->realm = strupper_talloc(cred, realm);
+                       if (cred->realm == NULL) {
+                               return NULL;
+                       }
+               }
+
                if (cred->realm_obtained == CRED_CALLBACK) {
                        cred->realm_obtained = CRED_CALLBACK_RESULT;
                        cli_credentials_invalidate_ccache(cred, 
cred->realm_obtained);
@@ -925,15 +1006,37 @@ _PUBLIC_ const char *cli_credentials_get_realm(struct 
cli_credentials *cred)
 }
 
 /**
- * Set the realm for this credentials context, and force it to
- * uppercase for the sanity of our local kerberos libraries
+ * @brief Set the realm for this credentials context.
+ *
+ * The realm be checked if it is ASCII only and upper cased if it isn't yet.
+ *
+ * @param cred The credential context.
+ *
+ * @param val  The realm to set or NULL to reset.
+ *
+ * @param obtained            This way the described realm was specified.
+ *
+ * @return true on success, false if the realm is not ASCII or the allocation
+ * failed.
  */
 _PUBLIC_ bool cli_credentials_set_realm(struct cli_credentials *cred,
-                              const char *val,
-                              enum credentials_obtained obtained)
+                                       const char *val,
+                                       enum credentials_obtained obtained)
 {
        if (obtained >= cred->realm_obtained) {
-               cred->realm = strupper_talloc(cred, val);
+               /* If `val = NULL` is passed, realm is reset */
+               cred->realm = NULL;
+               if (val != NULL) {
+                       /* For realm names, only ASCII is allowed */
+                       if (!str_is_ascii(val)) {
+                               return false;
+                       }
+
+                       cred->realm = strupper_talloc(cred, val);
+                       if (cred->realm == NULL) {
+                               return false;
+                       }
+               }
                cred->realm_obtained = obtained;
                cli_credentials_invalidate_ccache(cred, cred->realm_obtained);
                return true;
@@ -1030,8 +1133,6 @@ _PUBLIC_ void cli_credentials_parse_string(struct 
cli_credentials *credentials,
        }
 
        if ((p = strchr_m(uname,'@'))) {
-               char *x = NULL;
-
                /*
                 * We also need to set username and domain
                 * in order to undo the effect of
@@ -1040,11 +1141,6 @@ _PUBLIC_ void cli_credentials_parse_string(struct 
cli_credentials *credentials,
                cli_credentials_set_username(credentials, uname, obtained);
                cli_credentials_set_domain(credentials, "", obtained);
 
-               /* Make sure the realm is uppercase */
-               for (x = p + 1; x[0] != '\0'; x++) {
-                       *x = toupper_m(*x);
-               }
-
                cli_credentials_set_principal(credentials, uname, obtained);
                *p = 0;
                cli_credentials_set_realm(credentials, p+1, obtained);
@@ -1535,7 +1631,9 @@ _PUBLIC_ void 
cli_credentials_get_ntlm_username_domain(struct cli_credentials *c
                                              const char **username,
                                              const char **domain)
 {
-       if (cred->principal_obtained >= cred->username_obtained) {
+       if (!cli_credentials_is_anonymous(cred) &&
+           cred->principal_obtained >= cred->username_obtained)
+       {
                *domain = talloc_strdup(mem_ctx, "");
                *username = cli_credentials_get_principal(cred, mem_ctx);
        } else {
diff --git a/auth/credentials/credentials_cmdline.c 
b/auth/credentials/credentials_cmdline.c
index c8c7c183c22..e9cdc80d52a 100644
--- a/auth/credentials/credentials_cmdline.c
+++ b/auth/credentials/credentials_cmdline.c
@@ -46,6 +46,7 @@ static const char *cmdline_get_userpassword(struct 
cli_credentials *creds)
                goto fail;
        }
        talloc_set_name_const(ret, __location__);
+       talloc_keep_secret(ret);
 fail:
        ZERO_STRUCT(pwd);
        TALLOC_FREE(frame);
diff --git a/python/samba/tests/credentials.py 
b/python/samba/tests/credentials.py
index bc132681c48..1835d9b7b59 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -361,7 +361,7 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(creds.get_username(), "env_user")
         self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
         self.assertEqual(creds.get_realm(), realm.upper())
-        self.assertEqual(creds.get_principal(), "[email protected]")
+        self.assertEqual(creds.get_principal(), "[email protected]")
         creds.parse_string("domain\\user")
         self.assertEqual(creds.get_username(), "user")
         self.assertEqual(creds.get_domain(), "DOMAIN")
@@ -385,7 +385,7 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(creds.get_username(), "env_user")
         self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
         self.assertEqual(creds.get_realm(), realm.upper())
-        self.assertEqual(creds.get_principal(), "[email protected]")
+        self.assertEqual(creds.get_principal(), "[email protected]")
         creds.parse_string("domain\\user")
         self.assertEqual(creds.get_username(), "user")
         self.assertEqual(creds.get_domain(), "DOMAIN")
diff --git a/python/samba/tests/dcerpc/dfs.py b/python/samba/tests/dcerpc/dfs.py
new file mode 100644
index 00000000000..0fcce324e55
--- /dev/null
+++ b/python/samba/tests/dcerpc/dfs.py
@@ -0,0 +1,48 @@
+#
+# Unix SMB/CIFS implementation.
+# Copyright Ralph Boehme <[email protected]> 2025
+#
+# 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/>.
+#
+
+"""Tests for samba.dcerpc.dfs"""
+
+import os
+import logging
+import samba
+from samba.dcerpc import dfs
+from samba.tests import RpcInterfaceTestCase
+from samba.logger import get_samba_logger
+from samba.credentials import Credentials
+from samba.samba3 import libsmb_samba_internal as libsmb
+import samba.tests.libsmb
+from samba.samba3 import param as s3param
+
+logger = get_samba_logger(name=__name__)
+
+class DfsTests(samba.tests.libsmb.LibsmbTests):
+    def setUp(self):
+        super().setUp()
+        self.dfs = dfs.netdfs('ncacn_np:%s[/pipe/netdfs]' % self.server, 
self.lp, self.creds)
+        self.c = libsmb.Conn(self.server_ip, "msdfs-share", self.lp, 
self.creds)
+
+    def tearDown(self):
+        super().tearDown()
+
+    def test_dfs_reparse_tag(self):
+        self.dfs.Add('\\\\%s\\msdfs-share\\dfslink' % self.server, 
self.server, 'tmp', 'comment', 0)
+        l = self.c.list('', info_level=libsmb.SMB2_FIND_ID_BOTH_DIRECTORY_INFO)
+        files = {i['name']: i for i in l}
+        self.assertEqual(files['dfslink']['reparse_tag'], 
libsmb.IO_REPARSE_TAG_DFS)
+        self.dfs.Remove('\\\\%s\\msdfs-share\\dfslink' % self.server, 
self.server, 'tmp')
diff --git a/python/samba/tests/libsmb.py b/python/samba/tests/libsmb.py
index 3ac1b68a59b..e3683901df2 100644
--- a/python/samba/tests/libsmb.py
+++ b/python/samba/tests/libsmb.py
@@ -43,6 +43,7 @@ class LibsmbTests(samba.tests.TestCase):
         server_conf_dir = os.path.dirname(server_conf)
         self.global_inject = os.path.join(server_conf_dir, 
"global_inject.conf")
 
+        self.server = samba.tests.env_get_var_value("SERVER")
         self.server_ip = samba.tests.env_get_var_value("SERVER_IP")
 
     def clean_file(self, conn, filename):
diff --git a/source3/libsmb/pylibsmb.c b/source3/libsmb/pylibsmb.c
index 67872d8e3b1..cba910d173d 100644
--- a/source3/libsmb/pylibsmb.c
+++ b/source3/libsmb/pylibsmb.c
@@ -3740,6 +3740,7 @@ MODULE_INIT_FUNC(libsmb_samba_cwrapper)
        ADD_STRING(SMB2_CREATE_TAG_APP_INSTANCE_ID);
        ADD_STRING(SVHDX_OPEN_DEVICE_CONTEXT);
        ADD_STRING(SMB2_CREATE_TAG_POSIX);
+       ADD_FLAGS(SMB2_FIND_ID_BOTH_DIRECTORY_INFO);
        ADD_FLAGS(SMB2_FIND_POSIX_INFORMATION);
        ADD_FLAGS(FILE_SUPERSEDE);
        ADD_FLAGS(FILE_OPEN);
diff --git a/source3/modules/vfs_xattr_tdb.c b/source3/modules/vfs_xattr_tdb.c
index 447d868924d..19331d0de4f 100644
--- a/source3/modules/vfs_xattr_tdb.c
+++ b/source3/modules/vfs_xattr_tdb.c
@@ -604,13 +604,12 @@ static int xattr_tdb_unlinkat(vfs_handle_struct *handle,
        } else {
                ret = SMB_VFS_NEXT_STAT(handle, full_fname);
                if (ret == -1 && (errno == ENOENT || errno == ELOOP)) {
-                       if (VALID_STAT(smb_fname->st) &&
-                                       S_ISLNK(smb_fname->st.st_ex_mode)) {
-                               /*
-                                * Original name was a link - Could be
-                                * trying to remove a dangling symlink.
-                                */
-                               ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
+                       /*
+                        * Could be trying to remove a dangling symlink.
+                        */
+                       ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
+                       if (ret == 0 && !S_ISLNK(full_fname->st.st_ex_mode)) {


-- 
Samba Shared Repository

Reply via email to