Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fec4585f authored by Igor Mammedov's avatar Igor Mammedov Committed by Steve French
Browse files

CIFSGetDFSRefer cleanup + dfs_referral_level_3 fixed to conform REFERRAL_V3 the MS-DFSC spec.

parent de2db8d7
Loading
Loading
Loading
Loading
+7 −9
Original line number Diff line number Diff line
@@ -1906,17 +1906,15 @@ typedef struct smb_com_transaction2_get_dfs_refer_req {

typedef struct dfs_referral_level_3 {
	__le16 VersionNumber;
	__le16 ReferralSize;
	__le16 ServerType;	/* 0x0001 = CIFS server */
	__le16 ReferralFlags;	/* or proximity - not clear which since it is
				   always set to zero - SNIA spec says 0x01
				   means strip off PathConsumed chars before
				   submitting RequestFileName to remote node */
	__le16 TimeToLive;
	__le16 Proximity;
	__le16 Size;
	__le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */
	__le16 ReferralEntryFlags; /* 0x0200 bit set only for domain
				      or DC referral responce */
	__le32 TimeToLive;
	__le16 DfsPathOffset;
	__le16 DfsAlternatePathOffset;
	__le16 NetworkAddressOffset;
	__le16 NetworkAddressOffset; /* offset of the link target */
	__le16 ServiceSiteGuid;
} __attribute__((packed)) REFERRAL3;

typedef struct smb_com_transaction_get_dfs_refer_rsp {
+132 −73
Original line number Diff line number Diff line
@@ -81,6 +81,39 @@ static struct {
#endif /* CONFIG_CIFS_WEAK_PW_HASH */
#endif /* CIFS_POSIX */

/* Allocates buffer into dst and copies smb string from src to it.
 * caller is responsible for freeing dst if function returned 0.
 * returns:
 * 	on success - 0
 *	on failure - errno
 */
static int
cifs_strncpy_to_host(char **dst, const char *src, const int maxlen,
		 const bool is_unicode, const struct nls_table *nls_codepage)
{
	int plen;

	if (is_unicode) {
		plen = UniStrnlen((wchar_t *)src, maxlen);
		*dst = kmalloc(plen + 2, GFP_KERNEL);
		if (!*dst)
			goto cifs_strncpy_to_host_ErrExit;
		cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage);
	} else {
		plen = strnlen(src, maxlen);
		*dst = kmalloc(plen + 2, GFP_KERNEL);
		if (!*dst)
			goto cifs_strncpy_to_host_ErrExit;
		strncpy(*dst, src, plen);
	}
	(*dst)[plen] = 0;
	return 0;

cifs_strncpy_to_host_ErrExit:
	cERROR(1, ("Failed to allocate buffer for string\n"));
	return -ENOMEM;
}


/* Mark as invalid, all open files on tree connections since they
   were closed when session to server was lost */
@@ -3867,6 +3900,96 @@ CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
	return rc;
}

/* parses DFS refferal V3 structure
 * caller is responsible for freeing target_nodes
 * returns:
 * 	on success - 0
 *	on failure - errno
 */
static int
parse_DFS_REFERRALS(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
		unsigned int *num_of_nodes,
		struct dfs_info3_param **target_nodes,
		const struct nls_table *nls_codepage)
{
	int i, rc = 0;
	char *data_end;
	bool is_unicode;
	struct dfs_referral_level_3 *ref;

	is_unicode = pSMBr->hdr.Flags2 & SMBFLG2_UNICODE;
	*num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);

	if (*num_of_nodes < 1) {
		cERROR(1, ("num_referrals: must be at least > 0,"
			"but we get num_referrals = %d\n", *num_of_nodes));
		rc = -EINVAL;
		goto parse_DFS_REFERRALS_exit;
	}

	ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
	if (ref->VersionNumber != 3) {
		cERROR(1, ("Referrals of V%d version are not supported,"
			"should be V3", ref->VersionNumber));
		rc = -EINVAL;
		goto parse_DFS_REFERRALS_exit;
	}

	/* get the upper boundary of the resp buffer */
	data_end = (char *)(&(pSMBr->PathConsumed)) +
				le16_to_cpu(pSMBr->t2.DataCount);

	cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n",
			*num_of_nodes,
			le16_to_cpu(pSMBr->DFSFlags)));

	*target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
			*num_of_nodes, GFP_KERNEL);
	if (*target_nodes == NULL) {
		cERROR(1, ("Failed to allocate buffer for target_nodes\n"));
		rc = -ENOMEM;
		goto parse_DFS_REFERRALS_exit;
	}

	/* collect neccessary data from referrals */
	for (i = 0; i < *num_of_nodes; i++) {
		char *temp;
		int max_len;
		struct dfs_info3_param *node = (*target_nodes)+i;

		node->flags = le16_to_cpu(pSMBr->DFSFlags);
		node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
		node->server_type = le16_to_cpu(ref->ServerType);
		node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);

		/* copy DfsPath */
		temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
		max_len = data_end - temp;
		rc = cifs_strncpy_to_host(&(node->path_name), temp,
					max_len, is_unicode, nls_codepage);
		if (rc)
			goto parse_DFS_REFERRALS_exit;

		/* copy link target UNC */
		temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
		max_len = data_end - temp;
		rc = cifs_strncpy_to_host(&(node->node_name), temp,
					max_len, is_unicode, nls_codepage);
		if (rc)
			goto parse_DFS_REFERRALS_exit;

		ref += ref->Size;
	}

parse_DFS_REFERRALS_exit:
	if (rc) {
		free_dfs_info_array(*target_nodes, *num_of_nodes);
		*target_nodes = NULL;
		*num_of_nodes = 0;
	}
	return rc;
}

int
CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
		const unsigned char *searchName,
@@ -3877,12 +4000,9 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
/* TRANS2_GET_DFS_REFERRAL */
	TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
	TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
	struct dfs_referral_level_3 *referrals = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;
	unsigned int i;
	char *temp;
	__u16 params, byte_count;
	*num_of_nodes = 0;
	*target_nodes = NULL;
@@ -3960,80 +4080,19 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
	rc = validate_t2((struct smb_t2_rsp *)pSMBr);

	/* BB Also check if enough total bytes returned? */
	if (rc || (pSMBr->ByteCount < 17))
	if (rc || (pSMBr->ByteCount < 17)) {
		rc = -EIO;      /* bad smb */
	else {
		__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
		__u16 data_count = le16_to_cpu(pSMBr->t2.DataCount);

		cFYI(1, ("Decoding GetDFSRefer response BCC: %d  Offset %d",
			 pSMBr->ByteCount, data_offset));
		referrals =
		    (struct dfs_referral_level_3 *)
				(8 /* sizeof start of data block */ +
				data_offset +
				(char *) &pSMBr->hdr.Protocol);
		cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n"
			"for referral one refer size: 0x%x srv "
			"type: 0x%x refer flags: 0x%x ttl: 0x%x",
			le16_to_cpu(pSMBr->NumberOfReferrals),
			le16_to_cpu(pSMBr->DFSFlags),
			le16_to_cpu(referrals->ReferralSize),
			le16_to_cpu(referrals->ServerType),
			le16_to_cpu(referrals->ReferralFlags),
			le16_to_cpu(referrals->TimeToLive)));
		/* BB This field is actually two bytes in from start of
		   data block so we could do safety check that DataBlock
		   begins at address of pSMBr->NumberOfReferrals */
		*num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);

		/* BB Fix below so can return more than one referral */
		if (*num_of_nodes > 1)
			*num_of_nodes = 1;

		/* get the length of the strings describing refs */
		name_len = 0;
		for (i = 0; i < *num_of_nodes; i++) {
			/* make sure that DfsPathOffset not past end */
			__u16 offset = le16_to_cpu(referrals->DfsPathOffset);
			if (offset > data_count) {
				/* if invalid referral, stop here and do
				not try to copy any more */
				*num_of_nodes = i;
				break;
			}
			temp = ((char *)referrals) + offset;

			if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
				name_len += UniStrnlen((wchar_t *)temp,
							data_count);
			} else {
				name_len += strnlen(temp, data_count);
			}
			referrals++;
			/* BB add check that referral pointer does
			   not fall off end PDU */
		}
		/* BB add check for name_len bigger than bcc */
		*target_nodes =
			kmalloc(name_len+1+(*num_of_nodes),
				GFP_KERNEL);
		if (*target_nodes == NULL) {
			rc = -ENOMEM;
		goto GetDFSRefExit;
	}

		referrals = (struct dfs_referral_level_3 *)
				(8 /* sizeof data hdr */ + data_offset +
				(char *) &pSMBr->hdr.Protocol);
	cFYI(1, ("Decoding GetDFSRefer response BCC: %d  Offset %d",
				pSMBr->ByteCount,
				le16_to_cpu(pSMBr->t2.DataOffset)));

	/* parse returned result into more usable form */
	rc = parse_DFS_REFERRALS(pSMBr, num_of_nodes,
				 target_nodes, nls_codepage);

		for (i = 0; i < *num_of_nodes; i++) {
			temp = ((char *)referrals) +
				  le16_to_cpu(referrals->DfsPathOffset);
			/*  BB update target_uncs pointers */
			referrals++;
		}
	}
GetDFSRefExit:
	if (pSMB)
		cifs_buf_release(pSMB);