From patchwork Fri Jan 3 22:28:55 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paulo Alcantara X-Patchwork-Id: 13925968 Received: from mx.manguebit.com (mx.manguebit.com [167.235.159.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 398371917ED for ; Fri, 3 Jan 2025 22:29:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=167.235.159.17 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735943364; cv=none; b=IMC3OEL808jNBLbyaFZRrygfbUNZz8JzQ4jzx1u/YwXojmyVqUqYa47RfpeV4VQG6cm/+MqgZ/NhjfrqO0fE8xRVTkkFfXFKepMMgpzrHihi/lVS3me0XpFkNDZTtOkXtLLYdqpWeSAYO/gkZ/uFJX3Sph/EsgGC7/PZNmrjyjQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735943364; c=relaxed/simple; bh=JiPUhkh6gyobeUA8yUfYWq/dgfN29sKbusue6R4jLrE=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=OGtGWTzwxOnpdoueJho107XEf0R4+pRMyBU+Td7Wpw8vAVRHU9cguUZqgWfmGzuupg142Qu/rI068uYJYNjBoxUxC3VyOGGeiu/kPzfMC0JGc+tB/mJhZRQWRe3lYhNzrkvFCcTYAp1TfseA0KPBg7FOFvUKuvKQJ7wFiRuLQb0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=manguebit.com; spf=pass smtp.mailfrom=manguebit.com; dkim=pass (2048-bit key) header.d=manguebit.com header.i=@manguebit.com header.b=cFSuNb6S; arc=none smtp.client-ip=167.235.159.17 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=manguebit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=manguebit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=manguebit.com header.i=@manguebit.com header.b="cFSuNb6S" From: Paulo Alcantara DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=manguebit.com; s=dkim; t=1735943359; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=+KcThN5rTGpaX5TDR3GD1FbPQHxtkqdAmxzVVcmoXP8=; b=cFSuNb6ShDPNLuUmNgq9nC1tiLKwPCiDK+dIL+czZPjg7chjyB3+983ztmDxXqG33IoWea qpdK8ee2ci23PWGGWgoVBJgo1y9b/GNy27PNlv05FZbXo1wYUKStUcTSzfa8un68fJn/S5 Q/GTem9HOfB8NgToCglbCK2nQlWdRHd81gTmtcdv3L21sbQedP7lEonk9CqZx/4uNOrvhf EGzIs/3sD3TsEEcV1+dYvx695DJkP+sLcdw0po/hYX5QzHEFq2EpDWHrWilOp1jNmgDEnE /t8zu18XIDyClaLHWdrkQiNoRgACS0bw40xg9Y+c32SQ3yf1NdCUdktNBp91tw== To: smfrench@gmail.com Cc: linux-cifs@vger.kernel.org, Paulo Alcantara Subject: [PATCH 1/4] smb: client: introduce av_for_each_entry() helper Date: Fri, 3 Jan 2025 19:28:55 -0300 Message-ID: <20250103222858.87176-1-pc@manguebit.com> Precedence: bulk X-Mailing-List: linux-cifs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Use new helper in find_domain_name() and find_timestamp() to avoid duplicating code. Signed-off-by: Paulo Alcantara (Red Hat) --- 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 { From patchwork Fri Jan 3 22:28:56 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paulo Alcantara X-Patchwork-Id: 13925969 Received: from mx.manguebit.com (mx.manguebit.com [167.235.159.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 736C61C1AAA for ; Fri, 3 Jan 2025 22:29:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=167.235.159.17 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735943364; cv=none; b=DD+92BSKD2eP24rGR0pWEaq6ZjQEOf3PgKxozgtoMSFpX+ZwRKczeK/VpIez7Y8GNBHTKNB7dtTZdS2rbPXPE3ze8Xj4ufvRsdDKqD8GIWF6dQiXCsr21grqT69iONwsvM1oySiUubKhomwWqN7ADOzt66po2vAxclmBMvg5EbY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735943364; c=relaxed/simple; bh=Qu3EIwaOJxaXMuuTtMIaROkGFwDpEl8BG5j8qRcPFf8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ox4c6qLDTNVfYR0ka4vPJmQNMDmWMiOYhGk/MklqTULJZqRYnJugzwaDm8RKE21rP4Q/I69z5NItGUJjYp3ICejb3GNWHEjDj60sILa2Uz/THfkzKCw6i2MQxHWVWlFKMeh7b2nMLF4x3e8wggBZh8CTrL9qGWOvj7Pm2hvXr+I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=manguebit.com; spf=pass smtp.mailfrom=manguebit.com; dkim=pass (2048-bit key) header.d=manguebit.com header.i=@manguebit.com header.b=CGN5ecHq; arc=none smtp.client-ip=167.235.159.17 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=manguebit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=manguebit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=manguebit.com header.i=@manguebit.com header.b="CGN5ecHq" From: Paulo Alcantara DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=manguebit.com; s=dkim; t=1735943360; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZFGPStBVdO4K0GOx22DMvDVu/MX61EtFoDGohtYbQb4=; b=CGN5ecHqc91TAztsVB9gCfI6OUAsSTxzUrcuFVVgKAydWvjCQxZZg5YS8DdPmSZUdATvr0 k+Y5Z6cIhuljD1aEIMBIBHMZ2qpGgned3TkBv75HGo9QG6YA2h+KHXRxu5dqUSpZQbWQii wQTpXvG4U4z/Hnsp0t+Bi87um5vyjnoKjP66ONcrVA0nqvXyyX4JjHpNoo2/r9GKlEiedh ss1g1ISoF3vqA/65Dvp9GaM/KM7OA08kyGc/LcxUl0X/jpgGXuTmhJtsB5jLBZASFxGoJV DEcZEY99/5QU8c57L6hs27mcoIwf4jJKHMDe9T/1k5znTaBa+aw1CSd/FHyyYA== To: smfrench@gmail.com Cc: linux-cifs@vger.kernel.org, Paulo Alcantara Subject: [PATCH 2/4] smb: client: parse av pair type 4 in CHALLENGE_MESSAGE Date: Fri, 3 Jan 2025 19:28:56 -0300 Message-ID: <20250103222858.87176-2-pc@manguebit.com> In-Reply-To: <20250103222858.87176-1-pc@manguebit.com> References: <20250103222858.87176-1-pc@manguebit.com> Precedence: bulk X-Mailing-List: linux-cifs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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) --- 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, From patchwork Fri Jan 3 22:28:57 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paulo Alcantara X-Patchwork-Id: 13925970 Received: from mx.manguebit.com (mx.manguebit.com [167.235.159.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7C7121C1AC7 for ; Fri, 3 Jan 2025 22:29:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=167.235.159.17 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735943365; cv=none; b=aWrHRYrHCycSwHDki4datcpDRuM0Vp8d4ut7QVXL085TvEoex1XFnons2TEHn5Aun2+R4i7bztRpCUUEWpymAcH//s8evnY5P1V/yanxnTRRo1uek6dcjQTcQy8veynLD0SROL+xsZIcLNR6OvDkW5hQXK/aJkD23Ktyooxhqjk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735943365; c=relaxed/simple; bh=KyV78q/IYLA8OV0SwjEvuUIlq1VQOismLBskR3jrq70=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nAZLbYmlgMlyOL7V+FS4Rn5vkBZ7ht98MjxrHA/DVAsmuBBvGmwDwGsrnptX4kqBu+V/gLHErKG6Fk12Wav3Bn8k6OmC9zzFr3gH+UGPDwhk1KMTnFPN+FxjKGfTV+LYU9YXh/I5NElB0wP7DpBUuyTFEQh5C4L+n95uJHkOIm8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=manguebit.com; spf=pass smtp.mailfrom=manguebit.com; dkim=pass (2048-bit key) header.d=manguebit.com header.i=@manguebit.com header.b=m2UJyPP5; arc=none smtp.client-ip=167.235.159.17 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=manguebit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=manguebit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=manguebit.com header.i=@manguebit.com header.b="m2UJyPP5" From: Paulo Alcantara DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=manguebit.com; s=dkim; t=1735943361; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=nPz5FKkx38z1CYegdsq4N3HpyS5qu8iJxq4FTJ06Mac=; b=m2UJyPP5BwpuCXPN+E/6FV71gt5h7NEcPUbM6NIWibIu1m/qg+GfQ0b8wX2mkoB+wggiEu PFU5NehO6B6+yLAqUqXldLL2N2iP+GPZNj8UgZrhgY2w/kIPvq/sRu2jOiZZ/azu/YZV3+ hisjVY+94Gjmz9Tu62MNqkzA48+MdowTpTFKmqcZbhIGMWuvsC0IL4HEM5p0f938yb+an2 IiDm3Ao/F8WxExtzJ3bd4ZFxoWAJlpZ75rT6izh03zFkZAtrVX4NSVoy3nC7AEgkBYLuyj 6FwxyswlfScSuzL+NREh6qWhDG77pJ59jmLD/JRSqcSSJ5iSeHbyJLunwrLRvQ== To: smfrench@gmail.com Cc: linux-cifs@vger.kernel.org, Paulo Alcantara Subject: [PATCH 3/4] smb: client: fix DFS mount against old servers with NTLMSSP Date: Fri, 3 Jan 2025 19:28:57 -0300 Message-ID: <20250103222858.87176-3-pc@manguebit.com> In-Reply-To: <20250103222858.87176-1-pc@manguebit.com> References: <20250103222858.87176-1-pc@manguebit.com> Precedence: bulk X-Mailing-List: linux-cifs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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) --- 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 #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) From patchwork Fri Jan 3 22:25:34 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paulo Alcantara X-Patchwork-Id: 13925967 Received: from mx.manguebit.com (mx.manguebit.com [167.235.159.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7EB121BEF9D for ; Fri, 3 Jan 2025 22:25:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=167.235.159.17 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735943152; cv=none; b=Fu8ZVUbA/YDiLkG1+65goqbcVusiEKXexVNhVKS2PL3qC56sVSPRWMPBGHK1gnO/HIFaAVn/k1TkIvFaa2UWuy9dOvf8VycWlIzBUJzQmfXeypLdMrZOW/ZpDDhFKdxqkpEYAXztqbmCOIbYfMw7xV6D4lWLwZ7yj+L80vNiHRE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735943152; c=relaxed/simple; bh=WIeT1Z7Dg+dIjPbXwAeteAFm8T4TdcIr/DwE96jPWlQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RVthlvNMuUPLmY8gXFVcncdURd97lHacFF1haNlk7Nva8VtmPefTDxLKLbQisRgYRKDhOEjl8tqNoHj4yNnZz1KI2/XKhMS6IdU9lRUMPU6RlF5tbNcg1+gqL81PrXck6OgKQ4k5Rt54VYbhkHkbq5vDRXWWLbljQJpE8ZYSYac= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=manguebit.com; spf=pass smtp.mailfrom=manguebit.com; dkim=pass (2048-bit key) header.d=manguebit.com header.i=@manguebit.com header.b=F8Q5CIQp; arc=none smtp.client-ip=167.235.159.17 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=manguebit.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=manguebit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=manguebit.com header.i=@manguebit.com header.b="F8Q5CIQp" From: Paulo Alcantara DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=manguebit.com; s=dkim; t=1735943148; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Q4A0RciEHWvUEKXE9E8Jnh4UHYb1lakTO/Ck4veRFrM=; b=F8Q5CIQpkMDXA+BfU5dKxXWdfQ9qSi3YzOECR8yhlh5dk4rgnD7jXRjo6mFES9eTETpdJ1 Who6EPoT0hIhk+n2pIAtTpnnHxYCnyrmgsGORyC8TpMy2G+AJw5FFaKrqxnSq+uRTln+Fg LpbXQjEK6S+8GE8doYsy+toTsiMOpd+pMM4juwbCtTdRzdhE8f4BLWUp2dQFnVFYNcClLy A3qE2Qi8+KYiLBzBfaRSpLF7JXIZ9wyzXLYiCX45wDC6Pm+g1ADsyuZPMbwr0xRQAglJts e/lYIKYF7LDNUzOT+0Cy8AOLCzFddDJxblSqb7jzev9Q/Wm/SKf/4zhsGNtb+Q== To: smfrench@gmail.com Cc: linux-cifs@vger.kernel.org Subject: [PATCH 4/4] smb: client: parse DNS domain name from domain= option Date: Fri, 3 Jan 2025 19:25:34 -0300 Message-ID: <20250103222534.86744-4-pc@manguebit.com> In-Reply-To: <20250103222534.86744-1-pc@manguebit.com> References: <20250103222534.86744-1-pc@manguebit.com> Precedence: bulk X-Mailing-List: linux-cifs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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) --- 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));