[][openwrt][common][ksmbd][multiple security fix]
[Description]
Fix ksmbd multiple vulnerabilities include
CVE-2022-47943
CVE-2022-47938
CVE-2022-47942
CVE-2022-47941
also ref to https://www.cybersecurity-help.cz/vdb/SB2022122604
[Release-log]
N/A
Change-Id: Id51609671e347a3d59b047215a116b9bbcd43ba1
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7058417
diff --git a/openwrt_patches-21.02/151-ksmbd-multiple-vulnerabilities-fix.patch b/openwrt_patches-21.02/151-ksmbd-multiple-vulnerabilities-fix.patch
new file mode 100644
index 0000000..5e70b46
--- /dev/null
+++ b/openwrt_patches-21.02/151-ksmbd-multiple-vulnerabilities-fix.patch
@@ -0,0 +1,434 @@
+diff -Naur a/feeds/packages/kernel/ksmbd/patches/03-multiple_vulnerabilities_fix.patch b/feeds/packages/kernel/ksmbd/patches/03-multiple_vulnerabilities_fix.patch
+--- a/feeds/packages/kernel/ksmbd/patches/03-multiple_vulnerabilities_fix.patch 1970-01-01 08:00:00.000000000 +0800
++++ b/feeds/packages/kernel/ksmbd/patches/03-multiple_vulnerabilities_fix.patch 2023-01-17 17:31:25.943136641 +0800
+@@ -0,0 +1,430 @@
++diff -Naur a/smb2misc.c b/smb2misc.c
++--- a/smb2misc.c 2023-01-17 10:34:34.291983249 +0800
+++++ b/smb2misc.c 2023-01-17 15:17:16.924040490 +0800
++@@ -91,11 +91,6 @@
++ *off = 0;
++ *len = 0;
++
++- /* error reqeusts do not have data area */
++- if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED &&
++- (((struct smb2_err_rsp *)hdr)->StructureSize) == SMB2_ERROR_STRUCTURE_SIZE2_LE)
++- return ret;
++-
++ /*
++ * Following commands have data areas so we have to get the location
++ * of the data buffer offset and data buffer length for the particular
++@@ -137,8 +132,11 @@
++ *len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength);
++ break;
++ case SMB2_WRITE:
++- if (((struct smb2_write_req *)hdr)->DataOffset) {
++- *off = le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset);
+++ if (((struct smb2_write_req *)hdr)->DataOffset ||
+++ ((struct smb2_write_req *)hdr)->Length) {
+++ *off = max_t(unsigned int,
+++ le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset),
+++ offsetof(struct smb2_write_req, Buffer));
++ *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length);
++ break;
++ }
++diff -Naur a/smb2pdu.c b/smb2pdu.c
++--- a/smb2pdu.c 2023-01-17 10:37:20.251390488 +0800
+++++ b/smb2pdu.c 2023-01-17 17:25:58.076142662 +0800
++@@ -540,9 +540,10 @@
++ struct smb2_query_info_req *req;
++
++ req = smb2_get_msg(work->request_buf);
++- if (req->InfoType == SMB2_O_INFO_FILE &&
++- (req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
++- req->FileInfoClass == FILE_ALL_INFORMATION))
+++ if ((req->InfoType == SMB2_O_INFO_FILE &&
+++ (req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
+++ req->FileInfoClass == FILE_ALL_INFORMATION)) ||
+++ req->InfoType == SMB2_O_INFO_SECURITY)
++ sz = large_sz;
++ }
++
++@@ -1146,12 +1147,16 @@
++ status);
++ rsp->hdr.Status = status;
++ rc = -EINVAL;
+++ kfree(conn->preauth_info);
+++ conn->preauth_info = NULL;
++ goto err_out;
++ }
++
++ rc = init_smb3_11_server(conn);
++ if (rc < 0) {
++ rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+++ kfree(conn->preauth_info);
+++ conn->preauth_info = NULL;
++ goto err_out;
++ }
++
++@@ -3004,7 +3009,7 @@
++ goto err_out;
++
++ rc = build_sec_desc(user_ns,
++- pntsd, NULL,
+++ pntsd, NULL, 0,
++ OWNER_SECINFO |
++ GROUP_SECINFO |
++ DACL_SECINFO,
++@@ -3861,6 +3866,15 @@
++ return 0;
++ }
++
+++static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len)
+++{
+++ int free_len;
+++
+++ free_len = (int)(work->response_sz -
+++ (get_rfc1002_len(work->response_buf) + 4)) - hdr2_len;
+++ return free_len;
+++}
+++
++ static int smb2_calc_max_out_buf_len(struct ksmbd_work *work,
++ unsigned short hdr2_len,
++ unsigned int out_buf_len)
++@@ -3870,9 +3884,7 @@
++ if (out_buf_len > work->conn->vals->max_trans_size)
++ return -EINVAL;
++
++- free_len = (int)(work->response_sz -
++- (get_rfc1002_len(work->response_buf) + 4)) -
++- hdr2_len;
+++ free_len = smb2_resp_buf_len(work, hdr2_len);
++ if (free_len < 0)
++ return -EINVAL;
++
++@@ -5164,10 +5176,10 @@
++ struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL;
++ struct smb_fattr fattr = {{0}};
++ struct inode *inode;
++- __u32 secdesclen;
+++ __u32 secdesclen = 0;
++ unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
++ int addition_info = le32_to_cpu(req->AdditionalInformation);
++- int rc;
+++ int rc = 0, ppntsd_size = 0;
++
++ if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO |
++ PROTECTED_DACL_SECINFO |
++@@ -5213,11 +5225,14 @@
++
++ if (test_share_config_flag(work->tcon->share_conf,
++ KSMBD_SHARE_FLAG_ACL_XATTR))
++- ksmbd_vfs_get_sd_xattr(work->conn, user_ns,
++- fp->filp->f_path.dentry, &ppntsd);
++-
++- rc = build_sec_desc(user_ns, pntsd, ppntsd, addition_info,
++- &secdesclen, &fattr);
+++ ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns,
+++ fp->filp->f_path.dentry,
+++ &ppntsd);
+++
+++ /* Check if sd buffer size exceeds response buffer size */
+++ if (smb2_resp_buf_len(work, 8) > ppntsd_size)
+++ rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size,
+++ addition_info, &secdesclen, &fattr);
++ posix_acl_release(fattr.cf_acls);
++ posix_acl_release(fattr.cf_dacls);
++ kfree(ppntsd);
++@@ -6580,14 +6595,12 @@
++ writethrough = true;
++
++ if (is_rdma_channel == false) {
++- if ((u64)le16_to_cpu(req->DataOffset) + length >
++- get_rfc1002_len(work->request_buf)) {
++- pr_err("invalid write data offset %u, smb_len %u\n",
++- le16_to_cpu(req->DataOffset),
++- get_rfc1002_len(work->request_buf));
+++ if (le16_to_cpu(req->DataOffset) <
+++ offsetof(struct smb2_write_req, Buffer)) {
++ err = -EINVAL;
++ goto out;
++ }
+++
++ data_buf = (char *)(((char *)&req->hdr.ProtocolId) +
++ le16_to_cpu(req->DataOffset));
++
++diff -Naur a/smbacl.c b/smbacl.c
++--- a/smbacl.c 2023-01-17 16:38:30.244282066 +0800
+++++ b/smbacl.c 2023-01-17 17:03:41.689373609 +0800
++@@ -722,6 +722,7 @@
++ static void set_ntacl_dacl(struct user_namespace *user_ns,
++ struct smb_acl *pndacl,
++ struct smb_acl *nt_dacl,
+++ unsigned int aces_size,
++ const struct smb_sid *pownersid,
++ const struct smb_sid *pgrpsid,
++ struct smb_fattr *fattr)
++@@ -735,9 +736,19 @@
++ if (nt_num_aces) {
++ ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl));
++ for (i = 0; i < nt_num_aces; i++) {
++- memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size));
++- size += le16_to_cpu(ntace->size);
++- ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size));
+++ unsigned short nt_ace_size;
+++
+++ if (offsetof(struct smb_ace, access_req) > aces_size)
+++ break;
+++
+++ nt_ace_size = le16_to_cpu(ntace->size);
+++ if (nt_ace_size > aces_size)
+++ break;
+++
+++ memcpy((char *)pndace + size, ntace, nt_ace_size);
+++ size += nt_ace_size;
+++ aces_size -= nt_ace_size;
+++ ntace = (struct smb_ace *)((char *)ntace + nt_ace_size);
++ num_aces++;
++ }
++ }
++@@ -910,7 +921,7 @@
++ /* Convert permission bits from mode to equivalent CIFS ACL */
++ int build_sec_desc(struct user_namespace *user_ns,
++ struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd,
++- int addition_info, __u32 *secdesclen,
+++ int ppntsd_size, int addition_info, __u32 *secdesclen,
++ struct smb_fattr *fattr)
++ {
++ int rc = 0;
++@@ -970,15 +981,25 @@
++
++ if (!ppntsd) {
++ set_mode_dacl(user_ns, dacl_ptr, fattr);
++- } else if (!ppntsd->dacloffset) {
++- goto out;
++ } else {
++ struct smb_acl *ppdacl_ptr;
+++ unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset);
+++ int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset;
+++
+++ if (!dacl_offset ||
+++ (dacl_offset + sizeof(struct smb_acl) > ppntsd_size))
+++ goto out;
+++
+++ ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset);
+++ ppdacl_size = le16_to_cpu(ppdacl_ptr->size);
+++ if (ppdacl_size > ntacl_size ||
+++ ppdacl_size < sizeof(struct smb_acl))
+++ goto out;
++
++- ppdacl_ptr = (struct smb_acl *)((char *)ppntsd +
++- le32_to_cpu(ppntsd->dacloffset));
++ set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr,
++- nowner_sid_ptr, ngroup_sid_ptr, fattr);
+++ ntacl_size - sizeof(struct smb_acl),
+++ nowner_sid_ptr, ngroup_sid_ptr,
+++ fattr);
++ }
++ pntsd->dacloffset = cpu_to_le32(offset);
++ offset += le16_to_cpu(dacl_ptr->size);
++@@ -1012,24 +1033,31 @@
++ struct smb_sid owner_sid, group_sid;
++ struct dentry *parent = path->dentry->d_parent;
++ struct user_namespace *user_ns = mnt_user_ns(path->mnt);
++- int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0;
++- int rc = 0, num_aces, dacloffset, pntsd_type, acl_len;
+++ int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size;
+++ int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
++ char *aces_base;
++ bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
++
++- acl_len = ksmbd_vfs_get_sd_xattr(conn, user_ns,
++- parent, &parent_pntsd);
++- if (acl_len <= 0)
+++ pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
+++ parent, &parent_pntsd);
+++ if (pntsd_size <= 0)
++ return -ENOENT;
++ dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
++- if (!dacloffset) {
+++ if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
++ rc = -EINVAL;
++ goto free_parent_pntsd;
++ }
++
++ parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset);
+++ acl_len = pntsd_size - dacloffset;
++ num_aces = le32_to_cpu(parent_pdacl->num_aces);
++ pntsd_type = le16_to_cpu(parent_pntsd->type);
+++ pdacl_size = le16_to_cpu(parent_pdacl->size);
+++
+++ if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) {
+++ rc = -EINVAL;
+++ goto free_parent_pntsd;
+++ }
++
++ aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL);
++ if (!aces_base) {
++@@ -1040,11 +1068,23 @@
++ aces = (struct smb_ace *)aces_base;
++ parent_aces = (struct smb_ace *)((char *)parent_pdacl +
++ sizeof(struct smb_acl));
+++ aces_size = acl_len - sizeof(struct smb_acl);
++
++ if (pntsd_type & DACL_AUTO_INHERITED)
++ inherited_flags = INHERITED_ACE;
++
++ for (i = 0; i < num_aces; i++) {
+++ int pace_size;
+++
+++ if (offsetof(struct smb_ace, access_req) > aces_size)
+++ break;
+++
+++ pace_size = le16_to_cpu(parent_aces->size);
+++ if (pace_size > aces_size)
+++ break;
+++
+++ aces_size -= pace_size;
+++
++ flags = parent_aces->flags;
++ if (!smb_inherit_flags(flags, is_dir))
++ goto pass;
++@@ -1089,8 +1129,7 @@
++ aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size));
++ ace_cnt++;
++ pass:
++- parent_aces =
++- (struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size));
+++ parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size);
++ }
++
++ if (nt_size > 0) {
++@@ -1185,7 +1224,7 @@
++ struct smb_ntsd *pntsd = NULL;
++ struct smb_acl *pdacl;
++ struct posix_acl *posix_acls;
++- int rc = 0, acl_size;
+++ int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
++ struct smb_sid sid;
++ int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
++ struct smb_ace *ace;
++@@ -1194,37 +1233,33 @@
++ struct smb_ace *others_ace = NULL;
++ struct posix_acl_entry *pa_entry;
++ unsigned int sid_type = SIDOWNER;
++- char *end_of_acl;
+++ unsigned short ace_size;
++
++ ksmbd_debug(SMB, "check permission using windows acl\n");
++- acl_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
++- path->dentry, &pntsd);
++- if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) {
++- kfree(pntsd);
++- return 0;
++- }
+++ pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
+++ path->dentry, &pntsd);
+++ if (pntsd_size <= 0 || !pntsd)
+++ goto err_out;
+++
+++ dacl_offset = le32_to_cpu(pntsd->dacloffset);
+++ if (!dacl_offset ||
+++ (dacl_offset + sizeof(struct smb_acl) > pntsd_size))
+++ goto err_out;
++
++ pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
++- end_of_acl = ((char *)pntsd) + acl_size;
++- if (end_of_acl <= (char *)pdacl) {
++- kfree(pntsd);
++- return 0;
++- }
+++ acl_size = pntsd_size - dacl_offset;
+++ pdacl_size = le16_to_cpu(pdacl->size);
++
++- if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size) ||
++- le16_to_cpu(pdacl->size) < sizeof(struct smb_acl)) {
++- kfree(pntsd);
++- return 0;
++- }
+++ if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl))
+++ goto err_out;
++
++ if (!pdacl->num_aces) {
++- if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) &&
+++ if (!(pdacl_size - sizeof(struct smb_acl)) &&
++ *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) {
++ rc = -EACCES;
++ goto err_out;
++ }
++- kfree(pntsd);
++- return 0;
+++ goto err_out;
++ }
++
++ if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) {
++@@ -1232,11 +1267,16 @@
++ DELETE;
++
++ ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
+++ aces_size = acl_size - sizeof(struct smb_acl);
++ for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
+++ if (offsetof(struct smb_ace, access_req) > aces_size)
+++ break;
+++ ace_size = le16_to_cpu(ace->size);
+++ if (ace_size > aces_size)
+++ break;
+++ aces_size -= ace_size;
++ granted |= le32_to_cpu(ace->access_req);
++ ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size));
++- if (end_of_acl < (char *)ace)
++- goto err_out;
++ }
++
++ if (!pdacl->num_aces)
++@@ -1248,7 +1288,15 @@
++ id_to_sid(uid, sid_type, &sid);
++
++ ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
+++ aces_size = acl_size - sizeof(struct smb_acl);
++ for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
+++ if (offsetof(struct smb_ace, access_req) > aces_size)
+++ break;
+++ ace_size = le16_to_cpu(ace->size);
+++ if (ace_size > aces_size)
+++ break;
+++ aces_size -= ace_size;
+++
++ if (!compare_sids(&sid, &ace->sid) ||
++ !compare_sids(&sid_unix_NFS_mode, &ace->sid)) {
++ found = 1;
++@@ -1258,8 +1306,6 @@
++ others_ace = ace;
++
++ ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size));
++- if (end_of_acl < (char *)ace)
++- goto err_out;
++ }
++
++ if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) {
++diff -Naur a/smbacl.h b/smbacl.h
++--- a/smbacl.h 2023-01-17 17:09:11.618949941 +0800
+++++ b/smbacl.h 2023-01-17 17:10:10.681651570 +0800
++@@ -196,7 +196,7 @@
++ int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
++ int acl_len, struct smb_fattr *fattr);
++ int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
++- struct smb_ntsd *ppntsd, int addition_info,
+++ struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info,
++ __u32 *secdesclen, struct smb_fattr *fattr);
++ int init_acl_state(struct posix_acl_state *state, int cnt);
++ void free_acl_state(struct posix_acl_state *state);
++diff -Naur a/vfs.c b/vfs.c
++--- a/vfs.c 2023-01-17 17:10:33.910672093 +0800
+++++ b/vfs.c 2023-01-17 17:11:28.493111948 +0800
++@@ -1708,6 +1708,11 @@
++ }
++
++ *pntsd = acl.sd_buf;
+++ if (acl.sd_size < sizeof(struct smb_ntsd)) {
+++ pr_err("sd size is invalid\n");
+++ goto out_free;
+++ }
+++
++ (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) -
++ NDR_NTSD_OFFSETOF);
++ (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) -