mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 3/6] ksmbd: pass the domain of an NTLM login to user space
@ 2026-11-10  9:02 Jan Koester
  0 siblings, 0 replies; 2+ messages in thread
From: Jan Koester @ 2026-11-10  9:02 UTC (permalink / raw)
  To: linkinjeon, smfrench
  Cc: senozhatsky, tom, chenxiaosong, brauner, linux-cifs,
	linux-fsdevel, samba-technical, linux-kernel

The login request carries only the user name, though NTLMSSP's
AUTHENTICATE message names the domain too. A user space serving several
domains (or local accounts next to a domain's) can't tell DOMAIN\user
apart from OTHER\user, picks the first account of that name, and a
session that the domain controller then authenticates as one domain's
user runs with another account's ids.

Put the domain into the formerly reserved room of struct
ksmbd_login_request, flagged with KSMBD_LOGIN_REQ_DOMAIN, so the
structure keeps its size and older user space keeps working. Ask for
the login response extension by the account name user space answered
with: that one is unambiguous, the client's may not be.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jan Koester <jan.koester@tuxist.de>
---
 fs/smb/server/ksmbd_netlink.h    | 12 +++++++++++-
 fs/smb/server/mgmt/user_config.c | 16 ++++++++++++----
 fs/smb/server/mgmt/user_config.h |  2 +-
 fs/smb/server/smb2pdu.c          | 20 ++++++++++++++++----
 fs/smb/server/transport_ipc.c    | 12 +++++++++++-
 fs/smb/server/transport_ipc.h    |  2 +-
 6 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h
index 3fa1c7f801..3df5264ad7 100644
--- a/fs/smb/server/ksmbd_netlink.h
+++ b/fs/smb/server/ksmbd_netlink.h
@@ -66,6 +66,7 @@
 #define KSMBD_GENL_VERSION		0x01
 
 #define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ	48
+#define KSMBD_REQ_MAX_DOMAIN_NAME_SZ	48
 #define KSMBD_REQ_MAX_HASH_SZ		18
 #define KSMBD_REQ_MAX_SHARE_NAME	64
 
@@ -140,9 +141,18 @@ struct ksmbd_shutdown_request {
 struct ksmbd_login_request {
 	__u32	handle;
 	__s8	account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */
-	__u32	reserved[16];				/* Reserved room */
+	__u32	flags;				/* KSMBD_LOGIN_REQ_* */
+	__s8	domain[KSMBD_REQ_MAX_DOMAIN_NAME_SZ];	/* with _DOMAIN */
+	__u32	reserved[3];			/* Reserved room */
 };
 
+/*
+ * The domain the client named with the account (NTLMSSP DomainName): user
+ * space serving several domains can tell their accounts of the same name
+ * apart. Formerly reserved, so zero from older ksmbd.
+ */
+#define KSMBD_LOGIN_REQ_DOMAIN		BIT(0)
+
 /*
  * IPC user login response.
  */
diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c
index cb175a4c78..a25eb0647c 100644
--- a/fs/smb/server/mgmt/user_config.c
+++ b/fs/smb/server/mgmt/user_config.c
@@ -9,21 +9,29 @@
 #include "user_config.h"
 #include "../transport_ipc.h"
 
-struct ksmbd_user *ksmbd_login_user(const char *account)
+struct ksmbd_user *ksmbd_login_user(const char *account, const char *domain)
 {
 	struct ksmbd_login_response *resp;
 	struct ksmbd_login_response_ext *resp_ext = NULL;
 	struct ksmbd_user *user = NULL;
 
-	resp = ksmbd_ipc_login_request(account);
+	resp = ksmbd_ipc_login_request(account, domain);
 	if (!resp)
 		return NULL;
 
 	if (!(resp->status & KSMBD_USER_FLAG_OK))
 		goto out;
 
-	if (resp->status & KSMBD_USER_FLAG_EXTENSION)
-		resp_ext = ksmbd_ipc_login_request_ext(account);
+	/*
+	 * The extension of the account user space found: the name it
+	 * answered with is unambiguous, the one the client gave may not be.
+	 */
+	if (resp->status & KSMBD_USER_FLAG_EXTENSION) {
+		resp->account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ - 1] = '\0';
+		resp_ext = ksmbd_ipc_login_request_ext(resp->account[0] ?
+						       (const char *)resp->account :
+						       account);
+	}
 
 	user = ksmbd_alloc_user(resp, resp_ext);
 	kvfree(resp_ext);
diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h
index e55470e008..0035675e03 100644
--- a/fs/smb/server/mgmt/user_config.h
+++ b/fs/smb/server/mgmt/user_config.h
@@ -63,7 +63,7 @@ static inline unsigned int user_gid(struct ksmbd_user *user)
 	return user->gid;
 }
 
-struct ksmbd_user *ksmbd_login_user(const char *account);
+struct ksmbd_user *ksmbd_login_user(const char *account, const char *domain);
 struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
 		struct ksmbd_login_response_ext *resp_ext);
 void ksmbd_free_user(struct ksmbd_user *user);
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 8e47d725c1..db7f677b07 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2169,8 +2169,8 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn,
 {
 	struct authenticate_message *authblob;
 	struct ksmbd_user *user;
-	char *name;
-	unsigned int name_off, name_len, secbuf_len;
+	char *name, *domain = NULL;
+	unsigned int name_off, name_len, dom_off, dom_len, secbuf_len;
 
 	if (conn->use_spnego && conn->mechToken)
 		secbuf_len = conn->mechTokenLen;
@@ -2196,8 +2196,20 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn,
 		return NULL;
 	}
 
-	ksmbd_debug(SMB, "session setup request for user %s\n", name);
-	user = ksmbd_login_user(name);
+	/* DOMAIN\user: which of several domains' user of that name */
+	dom_off = le32_to_cpu(authblob->DomainName.BufferOffset);
+	dom_len = le16_to_cpu(authblob->DomainName.Length);
+	if (dom_len && secbuf_len >= (u64)dom_off + dom_len) {
+		domain = smb_strndup_from_utf16((const char *)authblob + dom_off,
+						dom_len, true, conn->local_nls);
+		if (IS_ERR(domain))
+			domain = NULL;
+	}
+
+	ksmbd_debug(SMB, "session setup request for user %s domain %s\n",
+		    name, domain ? domain : "");
+	user = ksmbd_login_user(name, domain);
+	kfree(domain);
 	kfree(name);
 	return user;
 }
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 7bbe1b3607..4d2f16b96e 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -648,7 +648,12 @@ static int ksmbd_ipc_heartbeat_request(void)
 	return ret;
 }
 
-struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account)
+/*
+ * @domain: the domain the client named (NULL or "": none); one too long to
+ * pass is left out, user space then decides by the account alone
+ */
+struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account,
+						     const char *domain)
 {
 	struct ksmbd_ipc_msg *msg;
 	struct ksmbd_login_request *req;
@@ -665,6 +670,11 @@ struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account)
 	req = (struct ksmbd_login_request *)msg->payload;
 	req->handle = ksmbd_acquire_id(&ipc_ida);
 	strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
+	if (domain && domain[0] &&
+	    strlen(domain) < KSMBD_REQ_MAX_DOMAIN_NAME_SZ) {
+		req->flags |= KSMBD_LOGIN_REQ_DOMAIN;
+		strscpy(req->domain, domain, KSMBD_REQ_MAX_DOMAIN_NAME_SZ);
+	}
 
 	resp = ipc_msg_send_request(msg, req->handle);
 	ipc_msg_handle_free(req->handle);
diff --git a/fs/smb/server/transport_ipc.h b/fs/smb/server/transport_ipc.h
index f957a33ae3..1aefa23264 100644
--- a/fs/smb/server/transport_ipc.h
+++ b/fs/smb/server/transport_ipc.h
@@ -11,7 +11,7 @@
 #define KSMBD_IPC_MAX_PAYLOAD	4096
 
 struct ksmbd_login_response *
-ksmbd_ipc_login_request(const char *account);
+ksmbd_ipc_login_request(const char *account, const char *domain);
 struct ksmbd_login_response_ext *
 ksmbd_ipc_login_request_ext(const char *account);
 
-- 
2.56.0

^ permalink raw reply	[flat|nested] 2+ messages in thread

* [RFC PATCH 3/6] ksmbd: pass the domain of an NTLM login to user space
  2026-10-28  6:19 [RFC PATCH 0/6] ksmbd: Windows SIDs for user and group ids Jan Koester
@ 2026-10-28  6:20 ` Jan Koester
  0 siblings, 0 replies; 2+ messages in thread
From: Jan Koester @ 2026-10-28  6:20 UTC (permalink / raw)
  To: Namjae Jeon, Steve French
  Cc: Sergey Senozhatsky, Tom Talpey, ChenXiaoSong, Christian Brauner,
	linux-cifs, linux-fsdevel, samba-technical, linux-kernel

The login request carries only the user name, though NTLMSSP's
AUTHENTICATE message names the domain too. A user space serving several
domains (or local accounts next to a domain's) can't tell DOMAIN\user
apart from OTHER\user, picks the first account of that name, and a
session that the domain controller then authenticates as one domain's
user runs with another account's ids.

Put the domain into the formerly reserved room of struct
ksmbd_login_request, flagged with KSMBD_LOGIN_REQ_DOMAIN, so the
structure keeps its size and older user space keeps working. Ask for
the login response extension by the account name user space answered
with: that one is unambiguous, the client's may not be.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jan Koester <jan.koester@tuxist.de>
---
 fs/smb/server/ksmbd_netlink.h    | 12 +++++++++++-
 fs/smb/server/mgmt/user_config.c | 16 ++++++++++++----
 fs/smb/server/mgmt/user_config.h |  2 +-
 fs/smb/server/smb2pdu.c          | 20 ++++++++++++++++----
 fs/smb/server/transport_ipc.c    | 12 +++++++++++-
 fs/smb/server/transport_ipc.h    |  2 +-
 6 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h
index 3fa1c7f801..3df5264ad7 100644
--- a/fs/smb/server/ksmbd_netlink.h
+++ b/fs/smb/server/ksmbd_netlink.h
@@ -66,6 +66,7 @@
 #define KSMBD_GENL_VERSION		0x01
 
 #define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ	48
+#define KSMBD_REQ_MAX_DOMAIN_NAME_SZ	48
 #define KSMBD_REQ_MAX_HASH_SZ		18
 #define KSMBD_REQ_MAX_SHARE_NAME	64
 
@@ -140,9 +141,18 @@ struct ksmbd_shutdown_request {
 struct ksmbd_login_request {
 	__u32	handle;
 	__s8	account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */
-	__u32	reserved[16];				/* Reserved room */
+	__u32	flags;				/* KSMBD_LOGIN_REQ_* */
+	__s8	domain[KSMBD_REQ_MAX_DOMAIN_NAME_SZ];	/* with _DOMAIN */
+	__u32	reserved[3];			/* Reserved room */
 };
 
+/*
+ * The domain the client named with the account (NTLMSSP DomainName): user
+ * space serving several domains can tell their accounts of the same name
+ * apart. Formerly reserved, so zero from older ksmbd.
+ */
+#define KSMBD_LOGIN_REQ_DOMAIN		BIT(0)
+
 /*
  * IPC user login response.
  */
diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c
index cb175a4c78..a25eb0647c 100644
--- a/fs/smb/server/mgmt/user_config.c
+++ b/fs/smb/server/mgmt/user_config.c
@@ -9,21 +9,29 @@
 #include "user_config.h"
 #include "../transport_ipc.h"
 
-struct ksmbd_user *ksmbd_login_user(const char *account)
+struct ksmbd_user *ksmbd_login_user(const char *account, const char *domain)
 {
 	struct ksmbd_login_response *resp;
 	struct ksmbd_login_response_ext *resp_ext = NULL;
 	struct ksmbd_user *user = NULL;
 
-	resp = ksmbd_ipc_login_request(account);
+	resp = ksmbd_ipc_login_request(account, domain);
 	if (!resp)
 		return NULL;
 
 	if (!(resp->status & KSMBD_USER_FLAG_OK))
 		goto out;
 
-	if (resp->status & KSMBD_USER_FLAG_EXTENSION)
-		resp_ext = ksmbd_ipc_login_request_ext(account);
+	/*
+	 * The extension of the account user space found: the name it
+	 * answered with is unambiguous, the one the client gave may not be.
+	 */
+	if (resp->status & KSMBD_USER_FLAG_EXTENSION) {
+		resp->account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ - 1] = '\0';
+		resp_ext = ksmbd_ipc_login_request_ext(resp->account[0] ?
+						       (const char *)resp->account :
+						       account);
+	}
 
 	user = ksmbd_alloc_user(resp, resp_ext);
 	kvfree(resp_ext);
diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h
index e55470e008..0035675e03 100644
--- a/fs/smb/server/mgmt/user_config.h
+++ b/fs/smb/server/mgmt/user_config.h
@@ -63,7 +63,7 @@ static inline unsigned int user_gid(struct ksmbd_user *user)
 	return user->gid;
 }
 
-struct ksmbd_user *ksmbd_login_user(const char *account);
+struct ksmbd_user *ksmbd_login_user(const char *account, const char *domain);
 struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
 		struct ksmbd_login_response_ext *resp_ext);
 void ksmbd_free_user(struct ksmbd_user *user);
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 8e47d725c1..db7f677b07 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -2169,8 +2169,8 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn,
 {
 	struct authenticate_message *authblob;
 	struct ksmbd_user *user;
-	char *name;
-	unsigned int name_off, name_len, secbuf_len;
+	char *name, *domain = NULL;
+	unsigned int name_off, name_len, dom_off, dom_len, secbuf_len;
 
 	if (conn->use_spnego && conn->mechToken)
 		secbuf_len = conn->mechTokenLen;
@@ -2196,8 +2196,20 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn,
 		return NULL;
 	}
 
-	ksmbd_debug(SMB, "session setup request for user %s\n", name);
-	user = ksmbd_login_user(name);
+	/* DOMAIN\user: which of several domains' user of that name */
+	dom_off = le32_to_cpu(authblob->DomainName.BufferOffset);
+	dom_len = le16_to_cpu(authblob->DomainName.Length);
+	if (dom_len && secbuf_len >= (u64)dom_off + dom_len) {
+		domain = smb_strndup_from_utf16((const char *)authblob + dom_off,
+						dom_len, true, conn->local_nls);
+		if (IS_ERR(domain))
+			domain = NULL;
+	}
+
+	ksmbd_debug(SMB, "session setup request for user %s domain %s\n",
+		    name, domain ? domain : "");
+	user = ksmbd_login_user(name, domain);
+	kfree(domain);
 	kfree(name);
 	return user;
 }
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 7bbe1b3607..4d2f16b96e 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -648,7 +648,12 @@ static int ksmbd_ipc_heartbeat_request(void)
 	return ret;
 }
 
-struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account)
+/*
+ * @domain: the domain the client named (NULL or "": none); one too long to
+ * pass is left out, user space then decides by the account alone
+ */
+struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account,
+						     const char *domain)
 {
 	struct ksmbd_ipc_msg *msg;
 	struct ksmbd_login_request *req;
@@ -665,6 +670,11 @@ struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account)
 	req = (struct ksmbd_login_request *)msg->payload;
 	req->handle = ksmbd_acquire_id(&ipc_ida);
 	strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
+	if (domain && domain[0] &&
+	    strlen(domain) < KSMBD_REQ_MAX_DOMAIN_NAME_SZ) {
+		req->flags |= KSMBD_LOGIN_REQ_DOMAIN;
+		strscpy(req->domain, domain, KSMBD_REQ_MAX_DOMAIN_NAME_SZ);
+	}
 
 	resp = ipc_msg_send_request(msg, req->handle);
 	ipc_msg_handle_free(req->handle);
diff --git a/fs/smb/server/transport_ipc.h b/fs/smb/server/transport_ipc.h
index f957a33ae3..1aefa23264 100644
--- a/fs/smb/server/transport_ipc.h
+++ b/fs/smb/server/transport_ipc.h
@@ -11,7 +11,7 @@
 #define KSMBD_IPC_MAX_PAYLOAD	4096
 
 struct ksmbd_login_response *
-ksmbd_ipc_login_request(const char *account);
+ksmbd_ipc_login_request(const char *account, const char *domain);
 struct ksmbd_login_response_ext *
 ksmbd_ipc_login_request_ext(const char *account);
 
-- 
2.56.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-10  9:02 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10  9:02 [RFC PATCH 3/6] ksmbd: pass the domain of an NTLM login to user space Jan Koester
  -- strict thread matches above, loose matches on Subject: below --
2026-10-28  6:19 [RFC PATCH 0/6] ksmbd: Windows SIDs for user and group ids Jan Koester
2026-10-28  6:20 ` [RFC PATCH 3/6] ksmbd: pass the domain of an NTLM login to user space Jan Koester

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®