From: Paulo Alcantara Date: Fri, 3 Jan 2025 19:52:25 +0000 (-0300) Subject: smb: client: parse av pair type 4 in CHALLENGE_MESSAGE X-Git-Tag: v6.14-rc1~127^2~21 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0e8ae9b953bc2c12aebd21e1e552e5deb1a0ff1e;p=thirdparty%2Fkernel%2Flinux.git smb: client: parse av pair type 4 in CHALLENGE_MESSAGE 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) Signed-off-by: Steve French --- diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c index 981897ec4dcd1..e69968e88fe72 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 6e63abe461fd2..e5982136e66f0 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 4373dd64b66d4..c23d5ba44caeb 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,