[network/kio-extras/release/26.08] smb: smb: fix DFS namespace authentication

Harald Sitter <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 753c547b35ab6b2cec8d65ca90f316c3c5aa486e by Harald Sitter.
Committed on 27/07/2026 at 17:36.
Pushed by sitter into branch 'release/26.08'.

smb: fix DFS namespace authentication

split and send the username and workgroup on our end while upstream is
not doing this reliably for the DFS scenario. should get revisited once
upstream has a fix out.

https://bugzilla.samba.org/show_bug.cgi?id=16149

BUG: 510902


(cherry picked from commit daa6a64cc4889bfbb03f4b41b08143506bfbdde7)

Co-authored-by: Clemens Brunner <[email protected]>

M  +24   -0    smb/autotests/smburltest.cpp
M  +11   -0    smb/smbauthenticator.cpp
M  +16   -0    smb/smburl.cpp
M  +7    -0    smb/smburl.h

https://invent.kde.org/network/kio-extras/-/commit/753c547b35ab6b2cec8d65ca90f316c3c5aa486e

diff --git a/smb/autotests/smburltest.cpp b/smb/autotests/smburltest.cpp
index 8d642231a..e8d6c996f 100644
--- a/smb/autotests/smburltest.cpp
+++ b/smb/autotests/smburltest.cpp
@@ -137,6 +137,30 @@ private Q_SLOTS:
         // We tag printers as such cause otherwise we have no way of knowing it was a printer.
         QCOMPARE(SMBUrl(QUrl("smb://host/printer?kio-printer=true")).getType(), SMBURLTYPE_PRINTER);
     }
+
+    void testSplitDomainUser()
+    {
+        // No separator: domain is empty, user is the whole string
+        QCOMPARE(SMBUrl::splitDomainUser("user"), std::make_pair(QString(), QString("user")));
+
+        // Forward slash separator
+        QCOMPARE(SMBUrl::splitDomainUser("DOMAIN/user"), std::make_pair(QString("DOMAIN"), QString("user")));
+
+        // Backslash separator
+        QCOMPARE(SMBUrl::splitDomainUser("DOMAIN\\user"), std::make_pair(QString("DOMAIN"), QString("user")));
+
+        // Both separators present: first one wins (forward slash before backslash)
+        QCOMPARE(SMBUrl::splitDomainUser("DOM/AIN\\user"), std::make_pair(QString("DOM"), QString("AIN\\user")));
+
+        // Both separators present: first one wins (backslash before forward slash)
+        QCOMPARE(SMBUrl::splitDomainUser("DOM\\AIN/user"), std::make_pair(QString("DOM"), QString("AIN/user")));
+
+        // Empty string
+        QCOMPARE(SMBUrl::splitDomainUser(QString()), std::make_pair(QString(), QString()));
+
+        // Separator at position 0: domain part is empty, falls back to no-domain path
+        QCOMPARE(SMBUrl::splitDomainUser("/user"), std::make_pair(QString(), QString("/user")));
+    }
 };
 
 QTEST_GUILESS_MAIN(SMBUrlTest)
diff --git a/smb/smbauthenticator.cpp b/smb/smbauthenticator.cpp
index 6afd08ed9..cc7e77301 100644
--- a/smb/smbauthenticator.cpp
+++ b/smb/smbauthenticator.cpp
@@ -74,6 +74,17 @@ void SMBAuthenticator::auth(SMBCCTX *context,
 
     if (m_frontend.checkCachedAuthentication(info)) {
         qCDebug(KIO_SMB_LOG) << "got password through cache" << info.username;
+        // Split "DOMAIN/user" or "DOMAIN\user" so the domain goes into the
+        // workgroup buffer and only the bare username into the username buffer.
+        // KIO encodes domain-qualified usernames as "DOMAIN/user" in the URL.
+        // TODO: revisit when https://bugzilla.samba.org/show_bug.cgi?id=16149 gets fixed properly
+        auto [domain, bareUser] = SMBUrl::splitDomainUser(info.username);
+        if (!domain.isEmpty()) {
+            strncpy(workgroup, domain.toUtf8().constData(), static_cast<size_t>(wgmaxlen - 1));
+            workgroup[wgmaxlen - 1] = '\0';
+        }
+        info.username = bareUser;
+        qCDebug(KIO_SMB_LOG) << "got password through cache: user=" << info.username << " domain=" << workgroup;
     } else if (!m_defaultUser.isEmpty()) {
         // user defined a default username/password in kcontrol; try this
         info.username = m_defaultUser;
diff --git a/smb/smburl.cpp b/smb/smburl.cpp
index 4436d28dd..42b2f7d1f 100644
--- a/smb/smburl.cpp
+++ b/smb/smburl.cpp
@@ -14,6 +14,22 @@
 #include <QHostAddress>
 #include <QUrlQuery>
 
+std::pair<QString, QString> SMBUrl::splitDomainUser(const QString &combined)
+{
+    const qsizetype slashPos = combined.indexOf(QLatin1Char('/'));
+    const qsizetype backslashPos = combined.indexOf(QLatin1Char('\\'));
+    qsizetype separatorPos = -1;
+    if (slashPos >= 0 && backslashPos >= 0) {
+        separatorPos = qMin(slashPos, backslashPos);
+    } else if (slashPos >= 0 || backslashPos >= 0) {
+        separatorPos = qMax(slashPos, backslashPos);
+    }
+    if (separatorPos > 0) {
+        return {combined.left(separatorPos), combined.mid(separatorPos + 1)};
+    }
+    return {{}, combined};
+}
+
 SMBUrl::SMBUrl(const QUrl &kurl)
     : QUrl(kurl)
 {
diff --git a/smb/smburl.h b/smb/smburl.h
index 14a0bf1a5..ad861c367 100644
--- a/smb/smburl.h
+++ b/smb/smburl.h
@@ -32,6 +32,7 @@
 #define KIO_SMB_INTERNAL_H_INCLUDED
 
 #include <QByteArray>
+#include <QString>
 #include <QUrl>
 
 /**
@@ -117,6 +118,12 @@ public:
      */
     SMBUrl partUrl() const;
 
+    /**
+     * Splits "DOMAIN/user" or "DOMAIN\user" into {domain, user}.
+     * domain is empty if there is no separator.
+     */
+    static std::pair<QString, QString> splitDomainUser(const QString &combined);
+
 private:
     void updateCache();
     QByteArray m_surl;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.