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

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

Signed-off-by: default avatarIgor Mammedov <niallain@gmail.com>
Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent de2db8d7
......@@ -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 {
......
......@@ -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 @@ GetInodeNumOut:
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 @@ getDFSRetry:
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment