CVE-2020-28374 (C, published 2021-01-13T04:15Z)
Description: value: In drivers/target/target_core_xcopy.c in the Linux kernel before 5.10.7, insufficient identifier checking in the LIO SCSI target code can be used by remote attackers to read or write files via directory traversal in an XCOPY request, aka CID-2896c93811e3. For example, an attack can occur over a network if the attacker has access to one iSCSI LUN. The attacker gains control over file access because I/O operations are proxied via an attacker-selected backstore.
Severity: MEDIUM (CVSS v3 base score: 8.1)
Weakness: CWE-22 \u2014 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
Repository: https://github.com/torvalds/linux
Files changed:
drivers/target/target_core_xcopy.c
drivers/target/target_core_xcopy.h
Commit message: scsi: target: Fix XCOPY NAA identifier lookup

When attempting to match EXTENDED COPY CSCD descriptors with corresponding
se_devices, target_xcopy_locate_se_dev_e4() currently iterates over LIO's
global devices list which includes all configured backstores.

This change ensures that only initiator-accessible backstores are
considered during CSCD descriptor lookup, according to the session's
se_node_acl LUN list.

To avoid LUN removal race conditions, device pinning is changed from being
configfs based to instead using the se_node_acl lun_ref.

Reference: CVE-2020-28374
Fixes: cbf031f425fd ("target: Add support for EXTENDED_COPY copy offload emulation")
Reviewed-by: Lee Duncan <lduncan@suse.com>
Signed-off-by: David Disseldorp <ddiss@suse.de>
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>


Vulnerable code:
struct xcopy_dev_search_info {
	const unsigned char *dev_wwn;
	struct se_device *found_dev;
};

					      void *data)
	struct xcopy_dev_search_info *info = data;
	if (!se_dev->dev_attrib.emulate_3pc)
	rc = memcmp(&tmp_dev_wwn[0], info->dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
	if (rc != 0)
		return 0;

	info->found_dev = se_dev;
	pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev);

	rc = target_depend_item(&se_dev->dev_group.cg_item);
		pr_err("configfs_depend_item attempt failed: %d for se_dev: %p\n",
		       rc, se_dev);
		return rc;
	pr_debug("Called configfs_depend_item for se_dev: %p se_dev->se_dev_group: %p\n",
		 se_dev, &se_dev->dev_group);
static int target_xcopy_locate_se_dev_e4(const unsigned char *dev_wwn,
					struct se_device **found_dev)
	struct xcopy_dev_search_info info;
	int ret;

	memset(&info, 0, sizeof(info));
	info.dev_wwn = dev_wwn;

	ret = target_for_each_device(target_xcopy_locate_se_dev_e4_iter, &info);
	if (ret == 1) {
		*found_dev = info.found_dev;
		return 0;
	} else {
		pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
		return -EINVAL;
		rc = target_xcopy_locate_se_dev_e4(xop->dst_tid_wwn,
						&xop->dst_dev);
		rc = target_xcopy_locate_se_dev_e4(xop->src_tid_wwn,
						&xop->src_dev);
	struct se_device *remote_dev;

		remote_dev = xop->dst_dev;
		remote_dev = xop->src_dev;

	pr_debug("Calling configfs_undepend_item for"
		  " remote_dev: %p remote_dev->dev_group: %p\n",
		  remote_dev, &remote_dev->dev_group.cg_item);
	target_undepend_item(&remote_dev->dev_group.cg_item);

Fixed code:
/**
 * target_xcopy_locate_se_dev_e4_iter - compare XCOPY NAA device identifiers
 *
 * @se_dev: device being considered for match
 * @dev_wwn: XCOPY requested NAA dev_wwn
 * @return: 1 on match, 0 on no-match
 */
					      const unsigned char *dev_wwn)
	if (!se_dev->dev_attrib.emulate_3pc) {
		pr_debug("XCOPY: emulate_3pc disabled on se_dev %p\n", se_dev);
	}
	rc = memcmp(&tmp_dev_wwn[0], dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
		pr_debug("XCOPY: skip non-matching: %*ph\n",
			 XCOPY_NAA_IEEE_REGEX_LEN, tmp_dev_wwn);
		return 0;
	pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev);
static int target_xcopy_locate_se_dev_e4(struct se_session *sess,
					const unsigned char *dev_wwn,
					struct se_device **_found_dev,
					struct percpu_ref **_found_lun_ref)
	struct se_dev_entry *deve;
	struct se_node_acl *nacl;
	struct se_lun *this_lun = NULL;
	struct se_device *found_dev = NULL;

	/* cmd with NULL sess indicates no associated $FABRIC_MOD */
	if (!sess)
		goto err_out;

	pr_debug("XCOPY 0xe4: searching for: %*ph\n",
		 XCOPY_NAA_IEEE_REGEX_LEN, dev_wwn);

	nacl = sess->se_node_acl;
	rcu_read_lock();
	hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) {
		struct se_device *this_dev;
		int rc;

		this_lun = rcu_dereference(deve->se_lun);
		this_dev = rcu_dereference_raw(this_lun->lun_se_dev);

		rc = target_xcopy_locate_se_dev_e4_iter(this_dev, dev_wwn);
		if (rc) {
			if (percpu_ref_tryget_live(&this_lun->lun_ref))
				found_dev = this_dev;
			break;
		}
	rcu_read_unlock();
	if (found_dev == NULL)
		goto err_out;

	pr_debug("lun_ref held for se_dev: %p se_dev->se_dev_group: %p\n",
		 found_dev, &found_dev->dev_group);
	*_found_dev = found_dev;
	*_found_lun_ref = &this_lun->lun_ref;
	return 0;
err_out:
	pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
	return -EINVAL;
		rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess,
						xop->dst_tid_wwn,
						&xop->dst_dev,
						&xop->remote_lun_ref);
		rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess,
						xop->src_tid_wwn,
						&xop->src_dev,
						&xop->remote_lun_ref);
		pr_debug("putting dst lun_ref for %p\n", xop->dst_dev);
		pr_debug("putting src lun_ref for %p\n", xop->src_dev);
	percpu_ref_put(xop->remote_lun_ref);
	struct percpu_ref *remote_lun_ref;