mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-17 20:25:19 +00:00
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Pull SMB3 fixes from Steve French: "Some small bug fixes as well as SMB2.1/SMB3 enablement for DFS (global namespace) which previously was only enabled for CIFS" * 'for-next' of git://git.samba.org/sfrench/cifs-2.6: smb2: Enforce sec= mount option CIFS: Fix sparse warnings CIFS: implement get_dfs_refer for SMB2+ CIFS: use DFS pathnames in SMB2+ Create requests CIFS: set signing flag in SMB2+ TreeConnect if needed CIFS: let ses->ipc_tid hold smb2 TreeIds CIFS: add use_ipc flag to SMB2_ioctl() CIFS: add build_path_from_dentry_optional_prefix() CIFS: move DFS response parsing out of SMB1 code CIFS: Fix possible use after free in demultiplex thread
This commit is contained in:
commit
0a040b2113
16 changed files with 446 additions and 167 deletions
|
@ -303,7 +303,9 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||||
* gives us the latter, so we must adjust the result.
|
* gives us the latter, so we must adjust the result.
|
||||||
*/
|
*/
|
||||||
mnt = ERR_PTR(-ENOMEM);
|
mnt = ERR_PTR(-ENOMEM);
|
||||||
full_path = build_path_from_dentry(mntpt);
|
|
||||||
|
/* always use tree name prefix */
|
||||||
|
full_path = build_path_from_dentry_optional_prefix(mntpt, true);
|
||||||
if (full_path == NULL)
|
if (full_path == NULL)
|
||||||
goto cdda_exit;
|
goto cdda_exit;
|
||||||
|
|
||||||
|
|
|
@ -130,10 +130,10 @@ wchar_t cifs_toupper(wchar_t in);
|
||||||
* Returns:
|
* Returns:
|
||||||
* Address of the first string
|
* Address of the first string
|
||||||
*/
|
*/
|
||||||
static inline wchar_t *
|
static inline __le16 *
|
||||||
UniStrcat(wchar_t *ucs1, const wchar_t *ucs2)
|
UniStrcat(__le16 *ucs1, const __le16 *ucs2)
|
||||||
{
|
{
|
||||||
wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */
|
__le16 *anchor = ucs1; /* save a pointer to start of ucs1 */
|
||||||
|
|
||||||
while (*ucs1++) ; /* To end of first string */
|
while (*ucs1++) ; /* To end of first string */
|
||||||
ucs1--; /* Return to the null */
|
ucs1--; /* Return to the null */
|
||||||
|
|
|
@ -443,6 +443,9 @@ struct smb_version_operations {
|
||||||
int (*is_transform_hdr)(void *buf);
|
int (*is_transform_hdr)(void *buf);
|
||||||
int (*receive_transform)(struct TCP_Server_Info *,
|
int (*receive_transform)(struct TCP_Server_Info *,
|
||||||
struct mid_q_entry **);
|
struct mid_q_entry **);
|
||||||
|
enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
|
||||||
|
enum securityEnum);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values {
|
struct smb_version_values {
|
||||||
|
@ -822,7 +825,7 @@ struct cifs_ses {
|
||||||
int ses_count; /* reference counter */
|
int ses_count; /* reference counter */
|
||||||
enum statusEnum status;
|
enum statusEnum status;
|
||||||
unsigned overrideSecFlg; /* if non-zero override global sec flags */
|
unsigned overrideSecFlg; /* if non-zero override global sec flags */
|
||||||
__u16 ipc_tid; /* special tid for connection to IPC share */
|
__u32 ipc_tid; /* special tid for connection to IPC share */
|
||||||
char *serverOS; /* name of operating system underlying server */
|
char *serverOS; /* name of operating system underlying server */
|
||||||
char *serverNOS; /* name of network operating system of server */
|
char *serverNOS; /* name of network operating system of server */
|
||||||
char *serverDomain; /* security realm of server */
|
char *serverDomain; /* security realm of server */
|
||||||
|
|
|
@ -2086,17 +2086,21 @@ typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */
|
||||||
__u8 ServiceSiteGuid[16]; /* MBZ, ignored */
|
__u8 ServiceSiteGuid[16]; /* MBZ, ignored */
|
||||||
} __attribute__((packed)) REFERRAL3;
|
} __attribute__((packed)) REFERRAL3;
|
||||||
|
|
||||||
typedef struct smb_com_transaction_get_dfs_refer_rsp {
|
struct get_dfs_referral_rsp {
|
||||||
struct smb_hdr hdr; /* wct = 10 */
|
|
||||||
struct trans2_resp t2;
|
|
||||||
__u16 ByteCount;
|
|
||||||
__u8 Pad;
|
|
||||||
__le16 PathConsumed;
|
__le16 PathConsumed;
|
||||||
__le16 NumberOfReferrals;
|
__le16 NumberOfReferrals;
|
||||||
__le32 DFSFlags;
|
__le32 DFSFlags;
|
||||||
REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */
|
REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */
|
||||||
/* followed by the strings pointed to by the referral structures */
|
/* followed by the strings pointed to by the referral structures */
|
||||||
} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_RSP;
|
} __packed;
|
||||||
|
|
||||||
|
typedef struct smb_com_transaction_get_dfs_refer_rsp {
|
||||||
|
struct smb_hdr hdr; /* wct = 10 */
|
||||||
|
struct trans2_resp t2;
|
||||||
|
__u16 ByteCount;
|
||||||
|
__u8 Pad;
|
||||||
|
struct get_dfs_referral_rsp dfs_data;
|
||||||
|
} __packed TRANSACTION2_GET_DFS_REFER_RSP;
|
||||||
|
|
||||||
/* DFS Flags */
|
/* DFS Flags */
|
||||||
#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */
|
#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */
|
||||||
|
|
|
@ -61,6 +61,8 @@ extern void exit_cifs_idmap(void);
|
||||||
extern int init_cifs_spnego(void);
|
extern int init_cifs_spnego(void);
|
||||||
extern void exit_cifs_spnego(void);
|
extern void exit_cifs_spnego(void);
|
||||||
extern char *build_path_from_dentry(struct dentry *);
|
extern char *build_path_from_dentry(struct dentry *);
|
||||||
|
extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry,
|
||||||
|
bool prefix);
|
||||||
extern char *cifs_build_path_to_root(struct smb_vol *vol,
|
extern char *cifs_build_path_to_root(struct smb_vol *vol,
|
||||||
struct cifs_sb_info *cifs_sb,
|
struct cifs_sb_info *cifs_sb,
|
||||||
struct cifs_tcon *tcon,
|
struct cifs_tcon *tcon,
|
||||||
|
@ -284,6 +286,11 @@ extern int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
|
||||||
const struct nls_table *nls_codepage,
|
const struct nls_table *nls_codepage,
|
||||||
unsigned int *num_referrals,
|
unsigned int *num_referrals,
|
||||||
struct dfs_info3_param **referrals, int remap);
|
struct dfs_info3_param **referrals, int remap);
|
||||||
|
extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
||||||
|
unsigned int *num_of_nodes,
|
||||||
|
struct dfs_info3_param **target_nodes,
|
||||||
|
const struct nls_table *nls_codepage, int remap,
|
||||||
|
const char *searchName, bool is_unicode);
|
||||||
extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
|
extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
|
||||||
struct cifs_sb_info *cifs_sb,
|
struct cifs_sb_info *cifs_sb,
|
||||||
struct smb_vol *vol);
|
struct smb_vol *vol);
|
||||||
|
@ -526,4 +533,6 @@ int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
|
||||||
int __cifs_calc_signature(struct smb_rqst *rqst,
|
int __cifs_calc_signature(struct smb_rqst *rqst,
|
||||||
struct TCP_Server_Info *server, char *signature,
|
struct TCP_Server_Info *server, char *signature,
|
||||||
struct shash_desc *shash);
|
struct shash_desc *shash);
|
||||||
|
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
|
||||||
|
enum securityEnum);
|
||||||
#endif /* _CIFSPROTO_H */
|
#endif /* _CIFSPROTO_H */
|
||||||
|
|
|
@ -4786,117 +4786,6 @@ GetInodeNumOut:
|
||||||
return rc;
|
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 remap,
|
|
||||||
const char *searchName)
|
|
||||||
{
|
|
||||||
int i, rc = 0;
|
|
||||||
char *data_end;
|
|
||||||
bool is_unicode;
|
|
||||||
struct dfs_referral_level_3 *ref;
|
|
||||||
|
|
||||||
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
|
|
||||||
is_unicode = true;
|
|
||||||
else
|
|
||||||
is_unicode = false;
|
|
||||||
*num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
|
|
||||||
|
|
||||||
if (*num_of_nodes < 1) {
|
|
||||||
cifs_dbg(VFS, "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 != cpu_to_le16(3)) {
|
|
||||||
cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n",
|
|
||||||
le16_to_cpu(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);
|
|
||||||
|
|
||||||
cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n",
|
|
||||||
*num_of_nodes, le32_to_cpu(pSMBr->DFSFlags));
|
|
||||||
|
|
||||||
*target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (*target_nodes == NULL) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto parse_DFS_referrals_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* collect necessary 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 = le32_to_cpu(pSMBr->DFSFlags);
|
|
||||||
if (is_unicode) {
|
|
||||||
__le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (tmp == NULL) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto parse_DFS_referrals_exit;
|
|
||||||
}
|
|
||||||
cifsConvertToUTF16((__le16 *) tmp, searchName,
|
|
||||||
PATH_MAX, nls_codepage, remap);
|
|
||||||
node->path_consumed = cifs_utf16_bytes(tmp,
|
|
||||||
le16_to_cpu(pSMBr->PathConsumed),
|
|
||||||
nls_codepage);
|
|
||||||
kfree(tmp);
|
|
||||||
} else
|
|
||||||
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;
|
|
||||||
node->path_name = cifs_strndup_from_utf16(temp, max_len,
|
|
||||||
is_unicode, nls_codepage);
|
|
||||||
if (!node->path_name) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto parse_DFS_referrals_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy link target UNC */
|
|
||||||
temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
|
|
||||||
max_len = data_end - temp;
|
|
||||||
node->node_name = cifs_strndup_from_utf16(temp, max_len,
|
|
||||||
is_unicode, nls_codepage);
|
|
||||||
if (!node->node_name) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto parse_DFS_referrals_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref++;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
int
|
||||||
CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
|
CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
|
||||||
const char *search_name, struct dfs_info3_param **target_nodes,
|
const char *search_name, struct dfs_info3_param **target_nodes,
|
||||||
|
@ -4993,9 +4882,11 @@ getDFSRetry:
|
||||||
get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset));
|
get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset));
|
||||||
|
|
||||||
/* parse returned result into more usable form */
|
/* parse returned result into more usable form */
|
||||||
rc = parse_DFS_referrals(pSMBr, num_of_nodes,
|
rc = parse_dfs_referrals(&pSMBr->dfs_data,
|
||||||
target_nodes, nls_codepage, remap,
|
le16_to_cpu(pSMBr->t2.DataCount),
|
||||||
search_name);
|
num_of_nodes, target_nodes, nls_codepage,
|
||||||
|
remap, search_name,
|
||||||
|
(pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) != 0);
|
||||||
|
|
||||||
GetDFSRefExit:
|
GetDFSRefExit:
|
||||||
cifs_buf_release(pSMB);
|
cifs_buf_release(pSMB);
|
||||||
|
|
|
@ -2074,7 +2074,8 @@ match_security(struct TCP_Server_Info *server, struct smb_vol *vol)
|
||||||
* that was specified, or "Unspecified" if that sectype was not
|
* that was specified, or "Unspecified" if that sectype was not
|
||||||
* compatible with the given NEGOTIATE request.
|
* compatible with the given NEGOTIATE request.
|
||||||
*/
|
*/
|
||||||
if (select_sectype(server, vol->sectype) == Unspecified)
|
if (server->ops->select_sectype(server, vol->sectype)
|
||||||
|
== Unspecified)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -80,6 +80,17 @@ cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
|
||||||
/* Note: caller must free return buffer */
|
/* Note: caller must free return buffer */
|
||||||
char *
|
char *
|
||||||
build_path_from_dentry(struct dentry *direntry)
|
build_path_from_dentry(struct dentry *direntry)
|
||||||
|
{
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
|
||||||
|
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||||
|
bool prefix = tcon->Flags & SMB_SHARE_IS_IN_DFS;
|
||||||
|
|
||||||
|
return build_path_from_dentry_optional_prefix(direntry,
|
||||||
|
prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
build_path_from_dentry_optional_prefix(struct dentry *direntry, bool prefix)
|
||||||
{
|
{
|
||||||
struct dentry *temp;
|
struct dentry *temp;
|
||||||
int namelen;
|
int namelen;
|
||||||
|
@ -92,7 +103,7 @@ build_path_from_dentry(struct dentry *direntry)
|
||||||
unsigned seq;
|
unsigned seq;
|
||||||
|
|
||||||
dirsep = CIFS_DIR_SEP(cifs_sb);
|
dirsep = CIFS_DIR_SEP(cifs_sb);
|
||||||
if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
|
if (prefix)
|
||||||
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
|
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
|
||||||
else
|
else
|
||||||
dfsplen = 0;
|
dfsplen = 0;
|
||||||
|
|
105
fs/cifs/misc.c
105
fs/cifs/misc.c
|
@ -640,3 +640,108 @@ cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
|
||||||
cifs_add_pending_open_locked(fid, tlink, open);
|
cifs_add_pending_open_locked(fid, tlink, open);
|
||||||
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
|
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* parses DFS refferal V3 structure
|
||||||
|
* caller is responsible for freeing target_nodes
|
||||||
|
* returns:
|
||||||
|
* - on success - 0
|
||||||
|
* - on failure - errno
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
||||||
|
unsigned int *num_of_nodes,
|
||||||
|
struct dfs_info3_param **target_nodes,
|
||||||
|
const struct nls_table *nls_codepage, int remap,
|
||||||
|
const char *searchName, bool is_unicode)
|
||||||
|
{
|
||||||
|
int i, rc = 0;
|
||||||
|
char *data_end;
|
||||||
|
struct dfs_referral_level_3 *ref;
|
||||||
|
|
||||||
|
*num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals);
|
||||||
|
|
||||||
|
if (*num_of_nodes < 1) {
|
||||||
|
cifs_dbg(VFS, "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 *) &(rsp->referrals);
|
||||||
|
if (ref->VersionNumber != cpu_to_le16(3)) {
|
||||||
|
cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n",
|
||||||
|
le16_to_cpu(ref->VersionNumber));
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto parse_DFS_referrals_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the upper boundary of the resp buffer */
|
||||||
|
data_end = (char *)rsp + rsp_size;
|
||||||
|
|
||||||
|
cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n",
|
||||||
|
*num_of_nodes, le32_to_cpu(rsp->DFSFlags));
|
||||||
|
|
||||||
|
*target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (*target_nodes == NULL) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto parse_DFS_referrals_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* collect necessary 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 = le32_to_cpu(rsp->DFSFlags);
|
||||||
|
if (is_unicode) {
|
||||||
|
__le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto parse_DFS_referrals_exit;
|
||||||
|
}
|
||||||
|
cifsConvertToUTF16((__le16 *) tmp, searchName,
|
||||||
|
PATH_MAX, nls_codepage, remap);
|
||||||
|
node->path_consumed = cifs_utf16_bytes(tmp,
|
||||||
|
le16_to_cpu(rsp->PathConsumed),
|
||||||
|
nls_codepage);
|
||||||
|
kfree(tmp);
|
||||||
|
} else
|
||||||
|
node->path_consumed = le16_to_cpu(rsp->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;
|
||||||
|
node->path_name = cifs_strndup_from_utf16(temp, max_len,
|
||||||
|
is_unicode, nls_codepage);
|
||||||
|
if (!node->path_name) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto parse_DFS_referrals_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy link target UNC */
|
||||||
|
temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
|
||||||
|
max_len = data_end - temp;
|
||||||
|
node->node_name = cifs_strndup_from_utf16(temp, max_len,
|
||||||
|
is_unicode, nls_codepage);
|
||||||
|
if (!node->node_name) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto parse_DFS_referrals_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref++;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_DFS_referrals_exit:
|
||||||
|
if (rc) {
|
||||||
|
free_dfs_info_array(*target_nodes, *num_of_nodes);
|
||||||
|
*target_nodes = NULL;
|
||||||
|
*num_of_nodes = 0;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
|
@ -498,7 +498,7 @@ setup_ntlmv2_ret:
|
||||||
}
|
}
|
||||||
|
|
||||||
enum securityEnum
|
enum securityEnum
|
||||||
select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
|
cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
|
||||||
{
|
{
|
||||||
switch (server->negflavor) {
|
switch (server->negflavor) {
|
||||||
case CIFS_NEGFLAVOR_EXTENDED:
|
case CIFS_NEGFLAVOR_EXTENDED:
|
||||||
|
@ -1391,7 +1391,7 @@ static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
|
||||||
{
|
{
|
||||||
int type;
|
int type;
|
||||||
|
|
||||||
type = select_sectype(ses->server, ses->sectype);
|
type = cifs_select_sectype(ses->server, ses->sectype);
|
||||||
cifs_dbg(FYI, "sess setup type %d\n", type);
|
cifs_dbg(FYI, "sess setup type %d\n", type);
|
||||||
if (type == Unspecified) {
|
if (type == Unspecified) {
|
||||||
cifs_dbg(VFS,
|
cifs_dbg(VFS,
|
||||||
|
|
|
@ -1087,6 +1087,7 @@ struct smb_version_operations smb1_operations = {
|
||||||
.is_read_op = cifs_is_read_op,
|
.is_read_op = cifs_is_read_op,
|
||||||
.wp_retry_size = cifs_wp_retry_size,
|
.wp_retry_size = cifs_wp_retry_size,
|
||||||
.dir_needs_close = cifs_dir_needs_close,
|
.dir_needs_close = cifs_dir_needs_close,
|
||||||
|
.select_sectype = cifs_select_sectype,
|
||||||
#ifdef CONFIG_CIFS_XATTR
|
#ifdef CONFIG_CIFS_XATTR
|
||||||
.query_all_EAs = CIFSSMBQAllEAs,
|
.query_all_EAs = CIFSSMBQAllEAs,
|
||||||
.set_EA = CIFSSMBSetEA,
|
.set_EA = CIFSSMBSetEA,
|
||||||
|
|
|
@ -73,7 +73,8 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
|
||||||
nr_ioctl_req.Timeout = 0; /* use server default (120 seconds) */
|
nr_ioctl_req.Timeout = 0; /* use server default (120 seconds) */
|
||||||
nr_ioctl_req.Reserved = 0;
|
nr_ioctl_req.Reserved = 0;
|
||||||
rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid,
|
rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid,
|
||||||
fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, true,
|
fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY,
|
||||||
|
true /* is_fsctl */, false /* use_ipc */,
|
||||||
(char *)&nr_ioctl_req, sizeof(nr_ioctl_req),
|
(char *)&nr_ioctl_req, sizeof(nr_ioctl_req),
|
||||||
NULL, NULL /* no return info */);
|
NULL, NULL /* no return info */);
|
||||||
if (rc == -EOPNOTSUPP) {
|
if (rc == -EOPNOTSUPP) {
|
||||||
|
|
|
@ -282,6 +282,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
|
||||||
|
|
||||||
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
||||||
FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
|
FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
|
||||||
|
false /* use_ipc */,
|
||||||
NULL /* no data input */, 0 /* no data input */,
|
NULL /* no data input */, 0 /* no data input */,
|
||||||
(char **)&out_buf, &ret_data_len);
|
(char **)&out_buf, &ret_data_len);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
|
@ -571,6 +572,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
|
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
|
||||||
FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */,
|
FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */,
|
||||||
|
false /* use_ipc */,
|
||||||
NULL, 0 /* no input */,
|
NULL, 0 /* no input */,
|
||||||
(char **)&res_key, &ret_data_len);
|
(char **)&res_key, &ret_data_len);
|
||||||
|
|
||||||
|
@ -635,7 +637,8 @@ smb2_clone_range(const unsigned int xid,
|
||||||
/* Request server copy to target from src identified by key */
|
/* Request server copy to target from src identified by key */
|
||||||
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
|
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
|
||||||
trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
|
trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
|
||||||
true /* is_fsctl */, (char *)pcchunk,
|
true /* is_fsctl */, false /* use_ipc */,
|
||||||
|
(char *)pcchunk,
|
||||||
sizeof(struct copychunk_ioctl), (char **)&retbuf,
|
sizeof(struct copychunk_ioctl), (char **)&retbuf,
|
||||||
&ret_data_len);
|
&ret_data_len);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
|
@ -787,7 +790,8 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||||
cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
|
cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
|
||||||
true /* is_fctl */, &setsparse, 1, NULL, NULL);
|
true /* is_fctl */, false /* use_ipc */,
|
||||||
|
&setsparse, 1, NULL, NULL);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
tcon->broken_sparse_sup = true;
|
tcon->broken_sparse_sup = true;
|
||||||
cifs_dbg(FYI, "set sparse rc = %d\n", rc);
|
cifs_dbg(FYI, "set sparse rc = %d\n", rc);
|
||||||
|
@ -857,7 +861,8 @@ smb2_duplicate_extents(const unsigned int xid,
|
||||||
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
|
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
|
||||||
trgtfile->fid.volatile_fid,
|
trgtfile->fid.volatile_fid,
|
||||||
FSCTL_DUPLICATE_EXTENTS_TO_FILE,
|
FSCTL_DUPLICATE_EXTENTS_TO_FILE,
|
||||||
true /* is_fsctl */, (char *)&dup_ext_buf,
|
true /* is_fsctl */, false /* use_ipc */,
|
||||||
|
(char *)&dup_ext_buf,
|
||||||
sizeof(struct duplicate_extents_to_file),
|
sizeof(struct duplicate_extents_to_file),
|
||||||
NULL,
|
NULL,
|
||||||
&ret_data_len);
|
&ret_data_len);
|
||||||
|
@ -891,7 +896,8 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||||
cfile->fid.volatile_fid,
|
cfile->fid.volatile_fid,
|
||||||
FSCTL_SET_INTEGRITY_INFORMATION,
|
FSCTL_SET_INTEGRITY_INFORMATION,
|
||||||
true /* is_fsctl */, (char *)&integr_info,
|
true /* is_fsctl */, false /* use_ipc */,
|
||||||
|
(char *)&integr_info,
|
||||||
sizeof(struct fsctl_set_integrity_information_req),
|
sizeof(struct fsctl_set_integrity_information_req),
|
||||||
NULL,
|
NULL,
|
||||||
&ret_data_len);
|
&ret_data_len);
|
||||||
|
@ -910,7 +916,8 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||||
cfile->fid.volatile_fid,
|
cfile->fid.volatile_fid,
|
||||||
FSCTL_SRV_ENUMERATE_SNAPSHOTS,
|
FSCTL_SRV_ENUMERATE_SNAPSHOTS,
|
||||||
true /* is_fsctl */, NULL, 0 /* no input data */,
|
true /* is_fsctl */, false /* use_ipc */,
|
||||||
|
NULL, 0 /* no input data */,
|
||||||
(char **)&retbuf,
|
(char **)&retbuf,
|
||||||
&ret_data_len);
|
&ret_data_len);
|
||||||
cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
|
cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
|
||||||
|
@ -1097,6 +1104,103 @@ smb2_new_lease_key(struct cifs_fid *fid)
|
||||||
generate_random_uuid(fid->lease_key);
|
generate_random_uuid(fid->lease_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
|
||||||
|
const char *search_name,
|
||||||
|
struct dfs_info3_param **target_nodes,
|
||||||
|
unsigned int *num_of_nodes,
|
||||||
|
const struct nls_table *nls_codepage, int remap)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
__le16 *utf16_path = NULL;
|
||||||
|
int utf16_path_len = 0;
|
||||||
|
struct cifs_tcon *tcon;
|
||||||
|
struct fsctl_get_dfs_referral_req *dfs_req = NULL;
|
||||||
|
struct get_dfs_referral_rsp *dfs_rsp = NULL;
|
||||||
|
u32 dfs_req_size = 0, dfs_rsp_size = 0;
|
||||||
|
|
||||||
|
cifs_dbg(FYI, "smb2_get_dfs_refer path <%s>\n", search_name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use any tcon from the current session. Here, the first one.
|
||||||
|
*/
|
||||||
|
spin_lock(&cifs_tcp_ses_lock);
|
||||||
|
tcon = list_first_entry_or_null(&ses->tcon_list, struct cifs_tcon,
|
||||||
|
tcon_list);
|
||||||
|
if (tcon)
|
||||||
|
tcon->tc_count++;
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
|
||||||
|
if (!tcon) {
|
||||||
|
cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n",
|
||||||
|
ses);
|
||||||
|
rc = -ENOTCONN;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX,
|
||||||
|
&utf16_path_len,
|
||||||
|
nls_codepage, remap);
|
||||||
|
if (!utf16_path) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
dfs_req_size = sizeof(*dfs_req) + utf16_path_len;
|
||||||
|
dfs_req = kzalloc(dfs_req_size, GFP_KERNEL);
|
||||||
|
if (!dfs_req) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Highest DFS referral version understood */
|
||||||
|
dfs_req->MaxReferralLevel = DFS_VERSION;
|
||||||
|
|
||||||
|
/* Path to resolve in an UTF-16 null-terminated string */
|
||||||
|
memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len);
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* try first with IPC */
|
||||||
|
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
||||||
|
FSCTL_DFS_GET_REFERRALS,
|
||||||
|
true /* is_fsctl */, true /* use_ipc */,
|
||||||
|
(char *)dfs_req, dfs_req_size,
|
||||||
|
(char **)&dfs_rsp, &dfs_rsp_size);
|
||||||
|
if (rc == -ENOTCONN) {
|
||||||
|
/* try with normal tcon */
|
||||||
|
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
||||||
|
FSCTL_DFS_GET_REFERRALS,
|
||||||
|
true /* is_fsctl */, false /*use_ipc*/,
|
||||||
|
(char *)dfs_req, dfs_req_size,
|
||||||
|
(char **)&dfs_rsp, &dfs_rsp_size);
|
||||||
|
}
|
||||||
|
} while (rc == -EAGAIN);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
cifs_dbg(VFS, "ioctl error in smb2_get_dfs_refer rc=%d\n", rc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size,
|
||||||
|
num_of_nodes, target_nodes,
|
||||||
|
nls_codepage, remap, search_name,
|
||||||
|
true /* is_unicode */);
|
||||||
|
if (rc) {
|
||||||
|
cifs_dbg(VFS, "parse error in smb2_get_dfs_refer rc=%d\n", rc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (tcon) {
|
||||||
|
spin_lock(&cifs_tcp_ses_lock);
|
||||||
|
tcon->tc_count--;
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
}
|
||||||
|
kfree(utf16_path);
|
||||||
|
kfree(dfs_req);
|
||||||
|
kfree(dfs_rsp);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
#define SMB2_SYMLINK_STRUCT_SIZE \
|
#define SMB2_SYMLINK_STRUCT_SIZE \
|
||||||
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
|
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
|
||||||
|
|
||||||
|
@ -1220,7 +1324,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||||
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
|
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
|
||||||
true /* is_fctl */, (char *)&fsctl_buf,
|
true /* is_fctl */, false /* use_ipc */,
|
||||||
|
(char *)&fsctl_buf,
|
||||||
sizeof(struct file_zero_data_information), NULL, NULL);
|
sizeof(struct file_zero_data_information), NULL, NULL);
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1254,7 +1359,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||||
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
|
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
|
||||||
true /* is_fctl */, (char *)&fsctl_buf,
|
true /* is_fctl */, false /* use_ipc */,
|
||||||
|
(char *)&fsctl_buf,
|
||||||
sizeof(struct file_zero_data_information), NULL, NULL);
|
sizeof(struct file_zero_data_information), NULL, NULL);
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1609,6 +1715,26 @@ static void cifs_crypt_complete(struct crypto_async_request *req, int err)
|
||||||
complete(&res->completion);
|
complete(&res->completion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
|
||||||
|
{
|
||||||
|
struct cifs_ses *ses;
|
||||||
|
u8 *ses_enc_key;
|
||||||
|
|
||||||
|
spin_lock(&cifs_tcp_ses_lock);
|
||||||
|
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
|
||||||
|
if (ses->Suid != ses_id)
|
||||||
|
continue;
|
||||||
|
ses_enc_key = enc ? ses->smb3encryptionkey :
|
||||||
|
ses->smb3decryptionkey;
|
||||||
|
memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE);
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Encrypt or decrypt @rqst message. @rqst has the following format:
|
* Encrypt or decrypt @rqst message. @rqst has the following format:
|
||||||
* iov[0] - transform header (associate data),
|
* iov[0] - transform header (associate data),
|
||||||
|
@ -1622,10 +1748,10 @@ crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
|
||||||
struct smb2_transform_hdr *tr_hdr =
|
struct smb2_transform_hdr *tr_hdr =
|
||||||
(struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base;
|
(struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base;
|
||||||
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24;
|
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24;
|
||||||
struct cifs_ses *ses;
|
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
u8 sign[SMB2_SIGNATURE_SIZE] = {};
|
u8 sign[SMB2_SIGNATURE_SIZE] = {};
|
||||||
|
u8 key[SMB3_SIGN_KEY_SIZE];
|
||||||
struct aead_request *req;
|
struct aead_request *req;
|
||||||
char *iv;
|
char *iv;
|
||||||
unsigned int iv_len;
|
unsigned int iv_len;
|
||||||
|
@ -1635,9 +1761,10 @@ crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
|
||||||
|
|
||||||
init_completion(&result.completion);
|
init_completion(&result.completion);
|
||||||
|
|
||||||
ses = smb2_find_smb_ses(server, tr_hdr->SessionId);
|
rc = smb2_get_enc_key(server, tr_hdr->SessionId, enc, key);
|
||||||
if (!ses) {
|
if (rc) {
|
||||||
cifs_dbg(VFS, "%s: Could not find session\n", __func__);
|
cifs_dbg(VFS, "%s: Could not get %scryption key\n", __func__,
|
||||||
|
enc ? "en" : "de");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1649,8 +1776,7 @@ crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
|
||||||
|
|
||||||
tfm = enc ? server->secmech.ccmaesencrypt :
|
tfm = enc ? server->secmech.ccmaesencrypt :
|
||||||
server->secmech.ccmaesdecrypt;
|
server->secmech.ccmaesdecrypt;
|
||||||
rc = crypto_aead_setkey(tfm, enc ? ses->smb3encryptionkey :
|
rc = crypto_aead_setkey(tfm, key, SMB3_SIGN_KEY_SIZE);
|
||||||
ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
cifs_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc);
|
cifs_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -2254,6 +2380,8 @@ struct smb_version_operations smb20_operations = {
|
||||||
.clone_range = smb2_clone_range,
|
.clone_range = smb2_clone_range,
|
||||||
.wp_retry_size = smb2_wp_retry_size,
|
.wp_retry_size = smb2_wp_retry_size,
|
||||||
.dir_needs_close = smb2_dir_needs_close,
|
.dir_needs_close = smb2_dir_needs_close,
|
||||||
|
.get_dfs_refer = smb2_get_dfs_refer,
|
||||||
|
.select_sectype = smb2_select_sectype,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_operations smb21_operations = {
|
struct smb_version_operations smb21_operations = {
|
||||||
|
@ -2335,6 +2463,8 @@ struct smb_version_operations smb21_operations = {
|
||||||
.wp_retry_size = smb2_wp_retry_size,
|
.wp_retry_size = smb2_wp_retry_size,
|
||||||
.dir_needs_close = smb2_dir_needs_close,
|
.dir_needs_close = smb2_dir_needs_close,
|
||||||
.enum_snapshots = smb3_enum_snapshots,
|
.enum_snapshots = smb3_enum_snapshots,
|
||||||
|
.get_dfs_refer = smb2_get_dfs_refer,
|
||||||
|
.select_sectype = smb2_select_sectype,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_operations smb30_operations = {
|
struct smb_version_operations smb30_operations = {
|
||||||
|
@ -2426,6 +2556,8 @@ struct smb_version_operations smb30_operations = {
|
||||||
.free_transform_rq = smb3_free_transform_rq,
|
.free_transform_rq = smb3_free_transform_rq,
|
||||||
.is_transform_hdr = smb3_is_transform_hdr,
|
.is_transform_hdr = smb3_is_transform_hdr,
|
||||||
.receive_transform = smb3_receive_transform,
|
.receive_transform = smb3_receive_transform,
|
||||||
|
.get_dfs_refer = smb2_get_dfs_refer,
|
||||||
|
.select_sectype = smb2_select_sectype,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_CIFS_SMB311
|
#ifdef CONFIG_CIFS_SMB311
|
||||||
|
@ -2518,6 +2650,8 @@ struct smb_version_operations smb311_operations = {
|
||||||
.free_transform_rq = smb3_free_transform_rq,
|
.free_transform_rq = smb3_free_transform_rq,
|
||||||
.is_transform_hdr = smb3_is_transform_hdr,
|
.is_transform_hdr = smb3_is_transform_hdr,
|
||||||
.receive_transform = smb3_receive_transform,
|
.receive_transform = smb3_receive_transform,
|
||||||
|
.get_dfs_refer = smb2_get_dfs_refer,
|
||||||
|
.select_sectype = smb2_select_sectype,
|
||||||
};
|
};
|
||||||
#endif /* CIFS_SMB311 */
|
#endif /* CIFS_SMB311 */
|
||||||
|
|
||||||
|
|
|
@ -620,6 +620,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
|
||||||
|
|
||||||
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
||||||
FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
|
FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
|
||||||
|
false /* use_ipc */,
|
||||||
(char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
|
(char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
|
||||||
(char **)&pneg_rsp, &rsplen);
|
(char **)&pneg_rsp, &rsplen);
|
||||||
|
|
||||||
|
@ -656,6 +657,28 @@ vneg_out:
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum securityEnum
|
||||||
|
smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
|
||||||
|
{
|
||||||
|
switch (requested) {
|
||||||
|
case Kerberos:
|
||||||
|
case RawNTLMSSP:
|
||||||
|
return requested;
|
||||||
|
case NTLMv2:
|
||||||
|
return RawNTLMSSP;
|
||||||
|
case Unspecified:
|
||||||
|
if (server->sec_ntlmssp &&
|
||||||
|
(global_secflags & CIFSSEC_MAY_NTLMSSP))
|
||||||
|
return RawNTLMSSP;
|
||||||
|
if ((server->sec_kerberos || server->sec_mskerberos) &&
|
||||||
|
(global_secflags & CIFSSEC_MAY_KRB5))
|
||||||
|
return Kerberos;
|
||||||
|
/* Fallthrough */
|
||||||
|
default:
|
||||||
|
return Unspecified;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SMB2_sess_data {
|
struct SMB2_sess_data {
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
struct cifs_ses *ses;
|
struct cifs_ses *ses;
|
||||||
|
@ -1008,10 +1031,17 @@ out:
|
||||||
static int
|
static int
|
||||||
SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
|
SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
|
||||||
{
|
{
|
||||||
if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
|
int type;
|
||||||
ses->sectype = RawNTLMSSP;
|
|
||||||
|
|
||||||
switch (ses->sectype) {
|
type = smb2_select_sectype(ses->server, ses->sectype);
|
||||||
|
cifs_dbg(FYI, "sess setup type %d\n", type);
|
||||||
|
if (type == Unspecified) {
|
||||||
|
cifs_dbg(VFS,
|
||||||
|
"Unable to select appropriate authentication method!");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
case Kerberos:
|
case Kerberos:
|
||||||
sess_data->func = SMB2_auth_kerberos;
|
sess_data->func = SMB2_auth_kerberos;
|
||||||
break;
|
break;
|
||||||
|
@ -1019,7 +1049,7 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
|
||||||
sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate;
|
sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cifs_dbg(VFS, "secType %d not supported!\n", ses->sectype);
|
cifs_dbg(VFS, "secType %d not supported!\n", type);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1167,8 +1197,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
|
||||||
|
|
||||||
/* since no tcon, smb2_init can not do this, so do here */
|
/* since no tcon, smb2_init can not do this, so do here */
|
||||||
req->hdr.sync_hdr.SessionId = ses->Suid;
|
req->hdr.sync_hdr.SessionId = ses->Suid;
|
||||||
/* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED)
|
if (ses->server->sign)
|
||||||
req->hdr.Flags |= SMB2_FLAGS_SIGNED; */
|
req->hdr.sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
|
||||||
} else if (encryption_required(tcon))
|
} else if (encryption_required(tcon))
|
||||||
flags |= CIFS_TRANSFORM_REQ;
|
flags |= CIFS_TRANSFORM_REQ;
|
||||||
|
|
||||||
|
@ -1527,6 +1557,51 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
|
||||||
|
const char *treename, const __le16 *path)
|
||||||
|
{
|
||||||
|
int treename_len, path_len;
|
||||||
|
struct nls_table *cp;
|
||||||
|
const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* skip leading "\\"
|
||||||
|
*/
|
||||||
|
treename_len = strlen(treename);
|
||||||
|
if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\'))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
treename += 2;
|
||||||
|
treename_len -= 2;
|
||||||
|
|
||||||
|
path_len = UniStrnlen((wchar_t *)path, PATH_MAX);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* make room for one path separator between the treename and
|
||||||
|
* path
|
||||||
|
*/
|
||||||
|
*out_len = treename_len + 1 + path_len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* final path needs to be null-terminated UTF16 with a
|
||||||
|
* size aligned to 8
|
||||||
|
*/
|
||||||
|
|
||||||
|
*out_size = roundup((*out_len+1)*2, 8);
|
||||||
|
*out_path = kzalloc(*out_size, GFP_KERNEL);
|
||||||
|
if (!*out_path)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
cp = load_nls_default();
|
||||||
|
cifs_strtoUTF16(*out_path, treename, treename_len, cp);
|
||||||
|
UniStrcat(*out_path, sep);
|
||||||
|
UniStrcat(*out_path, path);
|
||||||
|
unload_nls(cp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
||||||
__u8 *oplock, struct smb2_file_all_info *buf,
|
__u8 *oplock, struct smb2_file_all_info *buf,
|
||||||
|
@ -1575,30 +1650,49 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
||||||
req->ShareAccess = FILE_SHARE_ALL_LE;
|
req->ShareAccess = FILE_SHARE_ALL_LE;
|
||||||
req->CreateDisposition = cpu_to_le32(oparms->disposition);
|
req->CreateDisposition = cpu_to_le32(oparms->disposition);
|
||||||
req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
|
req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
|
||||||
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
|
|
||||||
/* do not count rfc1001 len field */
|
|
||||||
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
|
|
||||||
|
|
||||||
iov[0].iov_base = (char *)req;
|
iov[0].iov_base = (char *)req;
|
||||||
/* 4 for rfc1002 length field */
|
/* 4 for rfc1002 length field */
|
||||||
iov[0].iov_len = get_rfc1002_length(req) + 4;
|
iov[0].iov_len = get_rfc1002_length(req) + 4;
|
||||||
|
|
||||||
/* MUST set path len (NameLength) to 0 opening root of share */
|
|
||||||
req->NameLength = cpu_to_le16(uni_path_len - 2);
|
|
||||||
/* -1 since last byte is buf[0] which is sent below (path) */
|
/* -1 since last byte is buf[0] which is sent below (path) */
|
||||||
iov[0].iov_len--;
|
iov[0].iov_len--;
|
||||||
if (uni_path_len % 8 != 0) {
|
|
||||||
copy_size = uni_path_len / 8 * 8;
|
|
||||||
if (copy_size < uni_path_len)
|
|
||||||
copy_size += 8;
|
|
||||||
|
|
||||||
copy_path = kzalloc(copy_size, GFP_KERNEL);
|
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
|
||||||
if (!copy_path)
|
|
||||||
return -ENOMEM;
|
/* [MS-SMB2] 2.2.13 NameOffset:
|
||||||
memcpy((char *)copy_path, (const char *)path,
|
* If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
|
||||||
uni_path_len);
|
* the SMB2 header, the file name includes a prefix that will
|
||||||
|
* be processed during DFS name normalization as specified in
|
||||||
|
* section 3.3.5.9. Otherwise, the file name is relative to
|
||||||
|
* the share that is identified by the TreeId in the SMB2
|
||||||
|
* header.
|
||||||
|
*/
|
||||||
|
if (tcon->share_flags & SHI1005_FLAGS_DFS) {
|
||||||
|
int name_len;
|
||||||
|
|
||||||
|
req->hdr.sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
|
||||||
|
rc = alloc_path_with_tree_prefix(©_path, ©_size,
|
||||||
|
&name_len,
|
||||||
|
tcon->treeName, path);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
req->NameLength = cpu_to_le16(name_len * 2);
|
||||||
uni_path_len = copy_size;
|
uni_path_len = copy_size;
|
||||||
path = copy_path;
|
path = copy_path;
|
||||||
|
} else {
|
||||||
|
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
|
||||||
|
/* MUST set path len (NameLength) to 0 opening root of share */
|
||||||
|
req->NameLength = cpu_to_le16(uni_path_len - 2);
|
||||||
|
if (uni_path_len % 8 != 0) {
|
||||||
|
copy_size = roundup(uni_path_len, 8);
|
||||||
|
copy_path = kzalloc(copy_size, GFP_KERNEL);
|
||||||
|
if (!copy_path)
|
||||||
|
return -ENOMEM;
|
||||||
|
memcpy((char *)copy_path, (const char *)path,
|
||||||
|
uni_path_len);
|
||||||
|
uni_path_len = copy_size;
|
||||||
|
path = copy_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iov[1].iov_len = uni_path_len;
|
iov[1].iov_len = uni_path_len;
|
||||||
|
@ -1683,8 +1777,9 @@ creat_exit:
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||||
u64 volatile_fid, u32 opcode, bool is_fsctl, char *in_data,
|
u64 volatile_fid, u32 opcode, bool is_fsctl, bool use_ipc,
|
||||||
u32 indatalen, char **out_data, u32 *plen /* returned data len */)
|
char *in_data, u32 indatalen,
|
||||||
|
char **out_data, u32 *plen /* returned data len */)
|
||||||
{
|
{
|
||||||
struct smb2_ioctl_req *req;
|
struct smb2_ioctl_req *req;
|
||||||
struct smb2_ioctl_rsp *rsp;
|
struct smb2_ioctl_rsp *rsp;
|
||||||
|
@ -1721,6 +1816,16 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
if (use_ipc) {
|
||||||
|
if (ses->ipc_tid == 0) {
|
||||||
|
cifs_small_buf_release(req);
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
cifs_dbg(FYI, "replacing tid 0x%x with IPC tid 0x%x\n",
|
||||||
|
req->hdr.sync_hdr.TreeId, ses->ipc_tid);
|
||||||
|
req->hdr.sync_hdr.TreeId = ses->ipc_tid;
|
||||||
|
}
|
||||||
if (encryption_required(tcon))
|
if (encryption_required(tcon))
|
||||||
flags |= CIFS_TRANSFORM_REQ;
|
flags |= CIFS_TRANSFORM_REQ;
|
||||||
|
|
||||||
|
@ -1843,6 +1948,7 @@ SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
|
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
|
||||||
FSCTL_SET_COMPRESSION, true /* is_fsctl */,
|
FSCTL_SET_COMPRESSION, true /* is_fsctl */,
|
||||||
|
false /* use_ipc */,
|
||||||
(char *)&fsctl_input /* data input */,
|
(char *)&fsctl_input /* data input */,
|
||||||
2 /* in data len */, &ret_data /* out data */, NULL);
|
2 /* in data len */, &ret_data /* out data */, NULL);
|
||||||
|
|
||||||
|
|
|
@ -695,6 +695,14 @@ struct fsctl_get_integrity_information_rsp {
|
||||||
/* Integrity flags for above */
|
/* Integrity flags for above */
|
||||||
#define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001
|
#define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001
|
||||||
|
|
||||||
|
/* See MS-DFSC 2.2.2 */
|
||||||
|
struct fsctl_get_dfs_referral_req {
|
||||||
|
__le16 MaxReferralLevel;
|
||||||
|
__u8 RequestFileName[];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/* DFS response is struct get_dfs_refer_rsp */
|
||||||
|
|
||||||
/* See MS-SMB2 2.2.31.3 */
|
/* See MS-SMB2 2.2.31.3 */
|
||||||
struct network_resiliency_req {
|
struct network_resiliency_req {
|
||||||
__le32 Timeout;
|
__le32 Timeout;
|
||||||
|
|
|
@ -121,7 +121,8 @@ extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
|
||||||
struct smb2_err_rsp **err_buf);
|
struct smb2_err_rsp **err_buf);
|
||||||
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
u64 persistent_fid, u64 volatile_fid, u32 opcode,
|
u64 persistent_fid, u64 volatile_fid, u32 opcode,
|
||||||
bool is_fsctl, char *in_data, u32 indatalen,
|
bool is_fsctl, bool use_ipc,
|
||||||
|
char *in_data, u32 indatalen,
|
||||||
char **out_data, u32 *plen /* returned data len */);
|
char **out_data, u32 *plen /* returned data len */);
|
||||||
extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
u64 persistent_file_id, u64 volatile_file_id);
|
u64 persistent_file_id, u64 volatile_file_id);
|
||||||
|
@ -180,4 +181,6 @@ extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
__u8 *lease_key, const __le32 lease_state);
|
__u8 *lease_key, const __le32 lease_state);
|
||||||
extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
|
extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
|
||||||
|
|
||||||
|
extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *,
|
||||||
|
enum securityEnum);
|
||||||
#endif /* _SMB2PROTO_H */
|
#endif /* _SMB2PROTO_H */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue