* [PATCH 1/4] smb: client: introduce av_for_each_entry() helper
@ 2025-01-03 22:25 Paulo Alcantara
2025-01-03 22:25 ` [PATCH 2/4] smb: client: parse av pair type 4 in CHALLENGE_MESSAGE Paulo Alcantara
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Paulo Alcantara @ 2025-01-03 22:25 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs
Use new helper in find_domain_name() and find_timestamp() to avoid
duplicating code.
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
---
fs/smb/client/cifsencrypt.c | 123 ++++++++++++++++--------------------
fs/smb/client/cifspdu.h | 2 +-
2 files changed, 54 insertions(+), 71 deletions(-)
diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c
index 7a43daacc815..981897ec4dcd 100644
--- a/fs/smb/client/cifsencrypt.c
+++ b/fs/smb/client/cifsencrypt.c
@@ -315,6 +315,39 @@ build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)
return 0;
}
+#define AV_TYPE(av) (le16_to_cpu(av->type))
+#define AV_LEN(av) (le16_to_cpu(av->length))
+#define AV_DATA_PTR(av) ((void *)av->data)
+
+#define av_for_each_entry(ses, av) \
+ for (av = NULL; (av = find_next_av(ses, av));)
+
+static struct ntlmssp2_name *find_next_av(struct cifs_ses *ses,
+ struct ntlmssp2_name *av)
+{
+ u16 len;
+ u8 *end;
+
+ end = (u8 *)ses->auth_key.response + ses->auth_key.len;
+ if (!av) {
+ if (unlikely(!ses->auth_key.response || !ses->auth_key.len))
+ return NULL;
+ av = (void *)ses->auth_key.response;
+ } else {
+ av = (void *)((u8 *)av + sizeof(*av) + AV_LEN(av));
+ }
+
+ if ((u8 *)av + sizeof(*av) > end)
+ return NULL;
+
+ len = AV_LEN(av);
+ if (AV_TYPE(av) == NTLMSSP_AV_EOL)
+ return NULL;
+ if (!len || (u8 *)av + sizeof(*av) + len > end)
+ return NULL;
+ return av;
+}
+
/* Server has provided av pairs/target info in the type 2 challenge
* packet and we have plucked it and stored within smb session.
* We parse that blob here to find netbios domain name to be used
@@ -325,49 +358,23 @@ build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)
* may not fail against other (those who are not very particular
* about target string i.e. for some, just user name might suffice.
*/
-static int
-find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
+static int find_domain_name(struct cifs_ses *ses)
{
- unsigned int attrsize;
- unsigned int type;
- unsigned int onesize = sizeof(struct ntlmssp2_name);
- unsigned char *blobptr;
- unsigned char *blobend;
- struct ntlmssp2_name *attrptr;
+ const struct nls_table *nlsc = ses->local_nls;
+ struct ntlmssp2_name *av;
+ u16 len;
- if (!ses->auth_key.len || !ses->auth_key.response)
- return 0;
-
- blobptr = ses->auth_key.response;
- blobend = blobptr + ses->auth_key.len;
-
- while (blobptr + onesize < blobend) {
- attrptr = (struct ntlmssp2_name *) blobptr;
- type = le16_to_cpu(attrptr->type);
- if (type == NTLMSSP_AV_EOL)
- break;
- blobptr += 2; /* advance attr type */
- attrsize = le16_to_cpu(attrptr->length);
- blobptr += 2; /* advance attr size */
- if (blobptr + attrsize > blobend)
- break;
- if (type == NTLMSSP_AV_NB_DOMAIN_NAME) {
- if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN)
- break;
- if (!ses->domainName) {
- ses->domainName =
- kmalloc(attrsize + 1, GFP_KERNEL);
- if (!ses->domainName)
- return -ENOMEM;
- cifs_from_utf16(ses->domainName,
- (__le16 *)blobptr, attrsize, attrsize,
- nls_cp, NO_MAP_UNI_RSVD);
- break;
- }
+ av_for_each_entry(ses, av) {
+ len = AV_LEN(av);
+ if (AV_TYPE(av) == NTLMSSP_AV_NB_DOMAIN_NAME &&
+ len < CIFS_MAX_DOMAINNAME_LEN && !ses->domainName) {
+ ses->domainName = kmalloc(len + 1, GFP_KERNEL);
+ if (!ses->domainName)
+ return -ENOMEM;
+ cifs_from_utf16(ses->domainName, AV_DATA_PTR(av),
+ len, len, nlsc, NO_MAP_UNI_RSVD);
}
- blobptr += attrsize; /* advance attr value */
}
-
return 0;
}
@@ -377,40 +384,16 @@ find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
* as part of ntlmv2 authentication (or local current time as
* default in case of failure)
*/
-static __le64
-find_timestamp(struct cifs_ses *ses)
+static __le64 find_timestamp(struct cifs_ses *ses)
{
- unsigned int attrsize;
- unsigned int type;
- unsigned int onesize = sizeof(struct ntlmssp2_name);
- unsigned char *blobptr;
- unsigned char *blobend;
- struct ntlmssp2_name *attrptr;
+ struct ntlmssp2_name *av;
struct timespec64 ts;
- if (!ses->auth_key.len || !ses->auth_key.response)
- return 0;
-
- blobptr = ses->auth_key.response;
- blobend = blobptr + ses->auth_key.len;
-
- while (blobptr + onesize < blobend) {
- attrptr = (struct ntlmssp2_name *) blobptr;
- type = le16_to_cpu(attrptr->type);
- if (type == NTLMSSP_AV_EOL)
- break;
- blobptr += 2; /* advance attr type */
- attrsize = le16_to_cpu(attrptr->length);
- blobptr += 2; /* advance attr size */
- if (blobptr + attrsize > blobend)
- break;
- if (type == NTLMSSP_AV_TIMESTAMP) {
- if (attrsize == sizeof(u64))
- return *((__le64 *)blobptr);
- }
- blobptr += attrsize; /* advance attr value */
+ av_for_each_entry(ses, av) {
+ if (AV_TYPE(av) == NTLMSSP_AV_TIMESTAMP &&
+ AV_LEN(av) == sizeof(u64))
+ return *((__le64 *)AV_DATA_PTR(av));
}
-
ktime_get_real_ts64(&ts);
return cpu_to_le64(cifs_UnixTimeToNT(ts));
}
@@ -563,7 +546,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {
if (!ses->domainName) {
if (ses->domainAuto) {
- rc = find_domain_name(ses, nls_cp);
+ rc = find_domain_name(ses);
if (rc) {
cifs_dbg(VFS, "error %d finding domain name\n",
rc);
diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h
index ee78bb6741d6..17202754e6d0 100644
--- a/fs/smb/client/cifspdu.h
+++ b/fs/smb/client/cifspdu.h
@@ -649,7 +649,7 @@ typedef union smb_com_session_setup_andx {
struct ntlmssp2_name {
__le16 type;
__le16 length;
-/* char name[length]; */
+ __u8 data[];
} __attribute__((packed));
struct ntlmv2_resp {
--
2.47.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/4] smb: client: parse av pair type 4 in CHALLENGE_MESSAGE
2025-01-03 22:25 [PATCH 1/4] smb: client: introduce av_for_each_entry() helper Paulo Alcantara
@ 2025-01-03 22:25 ` Paulo Alcantara
2025-01-03 22:25 ` [PATCH 3/4] smb: client: fix DFS mount against old servers with NTLMSSP Paulo Alcantara
2025-01-03 22:25 ` [PATCH 4/4] smb: client: parse DNS domain name from domain= option Paulo Alcantara
2 siblings, 0 replies; 6+ messages in thread
From: Paulo Alcantara @ 2025-01-03 22:25 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs
Parse FQDN of the domain in CHALLENGE_MESSAGE message as it's gonna be
useful when mounting DFS shares against old Windows Servers (2012 R2
or earlier) that return not fully qualified hostnames for DFS targets
by default.
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
---
fs/smb/client/cifsencrypt.c | 63 ++++++++++++++++++++++++-------------
fs/smb/client/cifsglob.h | 1 +
fs/smb/client/misc.c | 1 +
3 files changed, 43 insertions(+), 22 deletions(-)
diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c
index 981897ec4dcd..e69968e88fe7 100644
--- a/fs/smb/client/cifsencrypt.c
+++ b/fs/smb/client/cifsencrypt.c
@@ -348,31 +348,37 @@ static struct ntlmssp2_name *find_next_av(struct cifs_ses *ses,
return av;
}
-/* Server has provided av pairs/target info in the type 2 challenge
- * packet and we have plucked it and stored within smb session.
- * We parse that blob here to find netbios domain name to be used
- * as part of ntlmv2 authentication (in Target String), if not already
- * specified on the command line.
- * If this function returns without any error but without fetching
- * domain name, authentication may fail against some server but
- * may not fail against other (those who are not very particular
- * about target string i.e. for some, just user name might suffice.
+/*
+ * Check if server has provided av pair of @type in the NTLMSSP
+ * CHALLENGE_MESSAGE blob.
*/
-static int find_domain_name(struct cifs_ses *ses)
+static int find_av_name(struct cifs_ses *ses, u16 type, char **name, u16 maxlen)
{
const struct nls_table *nlsc = ses->local_nls;
struct ntlmssp2_name *av;
- u16 len;
+ u16 len, nlen;
+
+ if (*name)
+ return 0;
av_for_each_entry(ses, av) {
len = AV_LEN(av);
- if (AV_TYPE(av) == NTLMSSP_AV_NB_DOMAIN_NAME &&
- len < CIFS_MAX_DOMAINNAME_LEN && !ses->domainName) {
- ses->domainName = kmalloc(len + 1, GFP_KERNEL);
- if (!ses->domainName)
+ if (AV_TYPE(av) != type)
+ continue;
+ if (!IS_ALIGNED(len, sizeof(__le16))) {
+ cifs_dbg(VFS | ONCE, "%s: bad length(%u) for type %u\n",
+ __func__, len, type);
+ continue;
+ }
+ nlen = len / sizeof(__le16);
+ if (nlen <= maxlen) {
+ ++nlen;
+ *name = kmalloc(nlen, GFP_KERNEL);
+ if (!*name)
return -ENOMEM;
- cifs_from_utf16(ses->domainName, AV_DATA_PTR(av),
- len, len, nlsc, NO_MAP_UNI_RSVD);
+ cifs_from_utf16(*name, AV_DATA_PTR(av), nlen,
+ len, nlsc, NO_MAP_UNI_RSVD);
+ break;
}
}
return 0;
@@ -546,16 +552,29 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {
if (!ses->domainName) {
if (ses->domainAuto) {
- rc = find_domain_name(ses);
- if (rc) {
- cifs_dbg(VFS, "error %d finding domain name\n",
- rc);
+ /*
+ * Domain (workgroup) hasn't been specified in
+ * mount options, so try to find it in
+ * CHALLENGE_MESSAGE message and then use it as
+ * part of NTLMv2 authentication.
+ */
+ rc = find_av_name(ses, NTLMSSP_AV_NB_DOMAIN_NAME,
+ &ses->domainName,
+ CIFS_MAX_DOMAINNAME_LEN);
+ if (rc)
goto setup_ntlmv2_rsp_ret;
- }
} else {
ses->domainName = kstrdup("", GFP_KERNEL);
+ if (!ses->domainName) {
+ rc = -ENOMEM;
+ goto setup_ntlmv2_rsp_ret;
+ }
}
}
+ rc = find_av_name(ses, NTLMSSP_AV_DNS_DOMAIN_NAME,
+ &ses->dns_dom, CIFS_MAX_DOMAINNAME_LEN);
+ if (rc)
+ goto setup_ntlmv2_rsp_ret;
} else {
rc = build_avpair_blob(ses, nls_cp);
if (rc) {
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 6e63abe461fd..e5982136e66f 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1154,6 +1154,7 @@ struct cifs_ses {
/* ========= end: protected by chan_lock ======== */
struct cifs_ses *dfs_root_ses;
struct nls_table *local_nls;
+ char *dns_dom; /* FQDN of the domain */
};
static inline bool
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index 4373dd64b66d..c23d5ba44cae 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -101,6 +101,7 @@ sesInfoFree(struct cifs_ses *buf_to_free)
kfree_sensitive(buf_to_free->password2);
kfree(buf_to_free->user_name);
kfree(buf_to_free->domainName);
+ kfree(buf_to_free->dns_dom);
kfree_sensitive(buf_to_free->auth_key.response);
spin_lock(&buf_to_free->iface_lock);
list_for_each_entry_safe(iface, niface, &buf_to_free->iface_list,
--
2.47.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/4] smb: client: fix DFS mount against old servers with NTLMSSP
2025-01-03 22:25 [PATCH 1/4] smb: client: introduce av_for_each_entry() helper Paulo Alcantara
2025-01-03 22:25 ` [PATCH 2/4] smb: client: parse av pair type 4 in CHALLENGE_MESSAGE Paulo Alcantara
@ 2025-01-03 22:25 ` Paulo Alcantara
2025-01-03 22:25 ` [PATCH 4/4] smb: client: parse DNS domain name from domain= option Paulo Alcantara
2 siblings, 0 replies; 6+ messages in thread
From: Paulo Alcantara @ 2025-01-03 22:25 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs
Old Windows servers will return not fully qualified DFS targets by
default as specified in
MS-DFSC 3.2.5.5 Receiving a Root Referral Request or Link Referral
Request
| Servers SHOULD<30> return fully qualified DNS host names of
| targets in responses to root referral requests and link referral
| requests.
| ...
| <30> Section 3.2.5.5: By default, Windows Server 2003, Windows
| Server 2008, Windows Server 2008 R2, Windows Server 2012, and
| Windows Server 2012 R2 return DNS host names that are not fully
| qualified for targets.
Fix this by converting all NetBIOS host names from DFS targets to
FQDNs and try resolving them first if DNS domain name was provided in
NTLMSSP CHALLENGE_MESSAGE message from previous SMB2_SESSION_SETUP.
This also prevents the client from translating the DFS target
hostnames to another domain depending on the network domain search
order.
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
---
fs/smb/client/cifsglob.h | 21 +++++++
fs/smb/client/connect.c | 5 +-
fs/smb/client/dfs.c | 13 ++--
fs/smb/client/dfs_cache.c | 3 +-
fs/smb/client/dns_resolve.c | 118 +++++++++++++++++++++---------------
fs/smb/client/dns_resolve.h | 3 +-
fs/smb/client/fs_context.c | 4 ++
fs/smb/client/fs_context.h | 1 +
fs/smb/client/misc.c | 3 +-
9 files changed, 113 insertions(+), 58 deletions(-)
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index e5982136e66f..c747b6f9baca 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -828,6 +828,7 @@ struct TCP_Server_Info {
*/
char *leaf_fullpath;
bool dfs_conn:1;
+ char dns_dom[CIFS_MAX_DOMAINNAME_LEN + 1];
};
static inline bool is_smb1(struct TCP_Server_Info *server)
@@ -2312,4 +2313,24 @@ static inline bool cifs_ses_exiting(struct cifs_ses *ses)
return ret;
}
+static inline bool cifs_netbios_name(const char *name, size_t namelen)
+{
+ bool ret = false;
+ size_t i;
+
+ if (namelen >= 1 && namelen <= RFC1001_NAME_LEN) {
+ for (i = 0; i < namelen; i++) {
+ const unsigned char c = name[i];
+
+ if (c == '\\' || c == '/' || c == ':' || c == '*' ||
+ c == '?' || c == '"' || c == '<' || c == '>' ||
+ c == '|' || c == '.')
+ return false;
+ if (!ret && isalpha(c))
+ ret = true;
+ }
+ }
+ return ret;
+}
+
#endif /* _CIFS_GLOB_H */
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index ddcc9e514a0e..d4b571d74191 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -97,7 +97,8 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
ss = server->dstaddr;
spin_unlock(&server->srv_lock);
- rc = dns_resolve_server_name_to_ip(unc, (struct sockaddr *)&ss, NULL);
+ rc = dns_resolve_server_name_to_ip(server->dns_dom, unc,
+ (struct sockaddr *)&ss, NULL);
kfree(unc);
if (rc < 0) {
@@ -1711,6 +1712,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
goto out_err;
}
}
+ if (ctx->dns_dom)
+ strscpy(tcp_ses->dns_dom, ctx->dns_dom);
if (ctx->nosharesock)
tcp_ses->nosharesock = true;
diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c
index 4647df9e1e3b..09d8808cd2e0 100644
--- a/fs/smb/client/dfs.c
+++ b/fs/smb/client/dfs.c
@@ -9,6 +9,8 @@
#include "fs_context.h"
#include "dfs.h"
+#define DFS_DOM(ctx) (ctx->dfs_root_ses ? ctx->dfs_root_ses->dns_dom : NULL)
+
/**
* dfs_parse_target_referral - set fs context for dfs target referral
*
@@ -46,8 +48,9 @@ int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_para
if (rc)
goto out;
- rc = dns_resolve_server_name_to_ip(path, (struct sockaddr *)&ctx->dstaddr, NULL);
-
+ rc = dns_resolve_server_name_to_ip(DFS_DOM(ctx), path,
+ (struct sockaddr *)&ctx->dstaddr,
+ NULL);
out:
kfree(path);
return rc;
@@ -59,8 +62,9 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
int rc;
ctx->leaf_fullpath = (char *)full_path;
+ ctx->dns_dom = DFS_DOM(ctx);
rc = cifs_mount_get_session(mnt_ctx);
- ctx->leaf_fullpath = NULL;
+ ctx->leaf_fullpath = ctx->dns_dom = NULL;
return rc;
}
@@ -264,7 +268,8 @@ static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
int rc = 0;
if (!ctx->nodfs && ctx->dfs_automount) {
- rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
+ rc = dns_resolve_server_name_to_ip(NULL, ctx->source,
+ addr, NULL);
if (!rc)
cifs_set_port(addr, ctx->port);
ctx->dfs_automount = false;
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index 541608b0267e..c0a167c869fb 100644
--- a/fs/smb/client/dfs_cache.c
+++ b/fs/smb/client/dfs_cache.c
@@ -1114,7 +1114,8 @@ static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)
extract_unc_hostname(s1, &host, &hostlen);
scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host);
- rc = dns_resolve_server_name_to_ip(unc, (struct sockaddr *)&ss, NULL);
+ rc = dns_resolve_server_name_to_ip(server->dns_dom, unc,
+ (struct sockaddr *)&ss, NULL);
if (rc < 0) {
cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n",
__func__, (int)hostlen, host);
diff --git a/fs/smb/client/dns_resolve.c b/fs/smb/client/dns_resolve.c
index 8bf8978bc5d6..83db27f9c8f1 100644
--- a/fs/smb/client/dns_resolve.c
+++ b/fs/smb/client/dns_resolve.c
@@ -20,69 +20,87 @@
#include "cifsproto.h"
#include "cifs_debug.h"
-/**
- * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
- * @unc: UNC path specifying the server (with '/' as delimiter)
- * @ip_addr: Where to return the IP address.
- * @expiry: Where to return the expiry time for the dns record.
- *
- * Returns zero success, -ve on error.
- */
-int
-dns_resolve_server_name_to_ip(const char *unc, struct sockaddr *ip_addr, time64_t *expiry)
+static int resolve_name(const char *name, size_t namelen,
+ struct sockaddr *addr, time64_t *expiry)
{
- const char *hostname, *sep;
char *ip;
- int len, rc;
+ int rc;
- if (!ip_addr || !unc)
- return -EINVAL;
-
- len = strlen(unc);
- if (len < 3) {
- cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc);
- return -EINVAL;
- }
-
- /* Discount leading slashes for cifs */
- len -= 2;
- hostname = unc + 2;
-
- /* Search for server name delimiter */
- sep = memchr(hostname, '/', len);
- if (sep)
- len = sep - hostname;
- else
- cifs_dbg(FYI, "%s: probably server name is whole unc: %s\n",
- __func__, unc);
-
- /* Try to interpret hostname as an IPv4 or IPv6 address */
- rc = cifs_convert_address(ip_addr, hostname, len);
- if (rc > 0) {
- cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %*.*s\n", __func__, len, len,
- hostname);
- return 0;
- }
-
- /* Perform the upcall */
- rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len,
- NULL, &ip, expiry, false);
+ rc = dns_query(current->nsproxy->net_ns, NULL, name,
+ namelen, NULL, &ip, expiry, false);
if (rc < 0) {
cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
- __func__, len, len, hostname);
+ __func__, (int)namelen, (int)namelen, name);
} else {
cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n",
- __func__, len, len, hostname, ip,
+ __func__, (int)namelen, (int)namelen, name, ip,
expiry ? (*expiry) : 0);
- rc = cifs_convert_address(ip_addr, ip, strlen(ip));
+ rc = cifs_convert_address(addr, ip, strlen(ip));
kfree(ip);
-
if (!rc) {
- cifs_dbg(FYI, "%s: unable to determine ip address\n", __func__);
+ cifs_dbg(FYI, "%s: unable to determine ip address\n",
+ __func__);
rc = -EHOSTUNREACH;
- } else
+ } else {
rc = 0;
+ }
}
return rc;
}
+
+/**
+ * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
+ * @dom: optional DNS domain name
+ * @unc: UNC path specifying the server (with '/' as delimiter)
+ * @ip_addr: Where to return the IP address.
+ * @expiry: Where to return the expiry time for the dns record.
+ *
+ * Returns zero success, -ve on error.
+ */
+int dns_resolve_server_name_to_ip(const char *dom, const char *unc,
+ struct sockaddr *ip_addr, time64_t *expiry)
+{
+ const char *name;
+ size_t namelen, len;
+ char *s;
+ int rc;
+
+ if (!ip_addr || !unc)
+ return -EINVAL;
+
+ cifs_dbg(FYI, "%s: dom=%s unc=%s\n", __func__, dom, unc);
+ if (strlen(unc) < 3)
+ return -EINVAL;
+
+ extract_unc_hostname(unc, &name, &namelen);
+ if (!namelen)
+ return -EINVAL;
+
+ cifs_dbg(FYI, "%s: hostname=%.*s\n", __func__, (int)namelen, name);
+ /* Try to interpret hostname as an IPv4 or IPv6 address */
+ rc = cifs_convert_address(ip_addr, name, namelen);
+ if (rc > 0) {
+ cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %*.*s\n",
+ __func__, (int)namelen, (int)namelen, name);
+ return 0;
+ }
+
+ /*
+ * If @name contains a NetBIOS name and @dom has been specified, then
+ * convert @name to an FQDN and try resolving it first.
+ */
+ if (dom && *dom && cifs_netbios_name(name, namelen)) {
+ len = strnlen(dom, CIFS_MAX_DOMAINNAME_LEN) + namelen + 2;
+ s = kmalloc(len, GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+
+ scnprintf(s, len, "%.*s.%s", (int)namelen, name, dom);
+ rc = resolve_name(s, len - 1, ip_addr, expiry);
+ kfree(s);
+ if (!rc)
+ return 0;
+ }
+ return resolve_name(name, namelen, ip_addr, expiry);
+}
diff --git a/fs/smb/client/dns_resolve.h b/fs/smb/client/dns_resolve.h
index 6eb0c15a2440..64c1dd2ad39b 100644
--- a/fs/smb/client/dns_resolve.h
+++ b/fs/smb/client/dns_resolve.h
@@ -14,7 +14,8 @@
#include <linux/net.h>
#ifdef __KERNEL__
-int dns_resolve_server_name_to_ip(const char *unc, struct sockaddr *ip_addr, time64_t *expiry);
+int dns_resolve_server_name_to_ip(const char *dom, const char *unc,
+ struct sockaddr *ip_addr, time64_t *expiry);
#endif /* KERNEL */
#endif /* _DNS_RESOLVE_H */
diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index 49123f458d0c..5381f05420bc 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -385,6 +385,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
new_ctx->source = NULL;
new_ctx->iocharset = NULL;
new_ctx->leaf_fullpath = NULL;
+ new_ctx->dns_dom = NULL;
/*
* Make sure to stay in sync with smb3_cleanup_fs_context_contents()
*/
@@ -399,6 +400,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
DUP_CTX_STR(nodename);
DUP_CTX_STR(iocharset);
DUP_CTX_STR(leaf_fullpath);
+ DUP_CTX_STR(dns_dom);
return 0;
}
@@ -1863,6 +1865,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx)
ctx->prepath = NULL;
kfree(ctx->leaf_fullpath);
ctx->leaf_fullpath = NULL;
+ kfree(ctx->dns_dom);
+ ctx->dns_dom = NULL;
}
void
diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
index ac6baa774ad3..8813533345ee 100644
--- a/fs/smb/client/fs_context.h
+++ b/fs/smb/client/fs_context.h
@@ -295,6 +295,7 @@ struct smb3_fs_context {
bool dfs_automount:1; /* set for dfs automount only */
enum cifs_reparse_type reparse_type;
bool dfs_conn:1; /* set for dfs mounts */
+ char *dns_dom;
};
extern const struct fs_parameter_spec smb3_fs_parameters[];
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index c23d5ba44cae..0d483cd08354 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -1189,7 +1189,8 @@ int match_target_ip(struct TCP_Server_Info *server,
cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2);
- rc = dns_resolve_server_name_to_ip(target, (struct sockaddr *)&ss, NULL);
+ rc = dns_resolve_server_name_to_ip(server->dns_dom, target,
+ (struct sockaddr *)&ss, NULL);
kfree(target);
if (rc < 0)
--
2.47.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/4] smb: client: parse DNS domain name from domain= option
2025-01-03 22:25 [PATCH 1/4] smb: client: introduce av_for_each_entry() helper Paulo Alcantara
2025-01-03 22:25 ` [PATCH 2/4] smb: client: parse av pair type 4 in CHALLENGE_MESSAGE Paulo Alcantara
2025-01-03 22:25 ` [PATCH 3/4] smb: client: fix DFS mount against old servers with NTLMSSP Paulo Alcantara
@ 2025-01-03 22:25 ` Paulo Alcantara
2025-01-03 22:32 ` Paulo Alcantara
2 siblings, 1 reply; 6+ messages in thread
From: Paulo Alcantara @ 2025-01-03 22:25 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs
If the user specified a DNS domain name in domain= mount option, then
use it instead of parsing it in NTLMSSP CHALLENGE_MESSAGE message.
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
---
fs/smb/client/connect.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index d4b571d74191..35d0243ae177 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -2280,12 +2280,13 @@ cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)),
struct cifs_ses *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
- int rc = 0;
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
+ struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
+ struct cifs_ses *ses;
+ unsigned int xid;
int retries = 0;
- unsigned int xid;
- struct cifs_ses *ses;
- struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
+ size_t len;
+ int rc = 0;
xid = get_xid();
@@ -2375,6 +2376,14 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL);
if (!ses->domainName)
goto get_ses_fail;
+
+ len = strnlen(ctx->domainname, CIFS_MAX_DOMAINNAME_LEN);
+ if (!cifs_netbios_name(ctx->domainname, len)) {
+ ses->dns_dom = kstrndup(ctx->domainname,
+ len, GFP_KERNEL);
+ if (!ses->dns_dom)
+ goto get_ses_fail;
+ }
}
strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name));
--
2.47.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/4] smb: client: parse DNS domain name from domain= option
2025-01-03 22:28 [PATCH 1/4] smb: client: introduce av_for_each_entry() helper Paulo Alcantara
@ 2025-01-03 22:28 ` Paulo Alcantara
0 siblings, 0 replies; 6+ messages in thread
From: Paulo Alcantara @ 2025-01-03 22:28 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs, Paulo Alcantara
If the user specified a DNS domain name in domain= mount option, then
use it instead of parsing it in NTLMSSP CHALLENGE_MESSAGE message.
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
---
fs/smb/client/connect.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index d4b571d74191..35d0243ae177 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -2280,12 +2280,13 @@ cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)),
struct cifs_ses *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
- int rc = 0;
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
+ struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
+ struct cifs_ses *ses;
+ unsigned int xid;
int retries = 0;
- unsigned int xid;
- struct cifs_ses *ses;
- struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
+ size_t len;
+ int rc = 0;
xid = get_xid();
@@ -2375,6 +2376,14 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL);
if (!ses->domainName)
goto get_ses_fail;
+
+ len = strnlen(ctx->domainname, CIFS_MAX_DOMAINNAME_LEN);
+ if (!cifs_netbios_name(ctx->domainname, len)) {
+ ses->dns_dom = kstrndup(ctx->domainname,
+ len, GFP_KERNEL);
+ if (!ses->dns_dom)
+ goto get_ses_fail;
+ }
}
strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name));
--
2.47.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 4/4] smb: client: parse DNS domain name from domain= option
2025-01-03 22:25 ` [PATCH 4/4] smb: client: parse DNS domain name from domain= option Paulo Alcantara
@ 2025-01-03 22:32 ` Paulo Alcantara
0 siblings, 0 replies; 6+ messages in thread
From: Paulo Alcantara @ 2025-01-03 22:32 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs
Paulo Alcantara <pc@manguebit.com> writes:
> If the user specified a DNS domain name in domain= mount option, then
> use it instead of parsing it in NTLMSSP CHALLENGE_MESSAGE message.
>
> Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
> ---
> fs/smb/client/connect.c | 19 ++++++++++++++-----
> 1 file changed, 14 insertions(+), 5 deletions(-)
Please ignore this patch. Resent the series [1].
[1] https://lore.kernel.org/r/20250103222858.87176-1-pc@manguebit.com
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-01-03 22:32 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-03 22:25 [PATCH 1/4] smb: client: introduce av_for_each_entry() helper Paulo Alcantara
2025-01-03 22:25 ` [PATCH 2/4] smb: client: parse av pair type 4 in CHALLENGE_MESSAGE Paulo Alcantara
2025-01-03 22:25 ` [PATCH 3/4] smb: client: fix DFS mount against old servers with NTLMSSP Paulo Alcantara
2025-01-03 22:25 ` [PATCH 4/4] smb: client: parse DNS domain name from domain= option Paulo Alcantara
2025-01-03 22:32 ` Paulo Alcantara
-- strict thread matches above, loose matches on Subject: below --
2025-01-03 22:28 [PATCH 1/4] smb: client: introduce av_for_each_entry() helper Paulo Alcantara
2025-01-03 22:28 ` [PATCH 4/4] smb: client: parse DNS domain name from domain= option Paulo Alcantara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox