|
|
|
@ -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; |
|
|
|
|
goto GetDFSRefExit; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d", |
|
|
|
|
pSMBr->ByteCount, |
|
|
|
|
le16_to_cpu(pSMBr->t2.DataOffset))); |
|
|
|
|
|
|
|
|
|
referrals = (struct dfs_referral_level_3 *) |
|
|
|
|
(8 /* sizeof data hdr */ + data_offset + |
|
|
|
|
(char *) &pSMBr->hdr.Protocol); |
|
|
|
|
/* 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); |
|
|
|
|