Skip to content

Commit

Permalink
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Browse files Browse the repository at this point in the history
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
  • Loading branch information
torvalds committed Mar 4, 2017
2 parents 4e66c42 + ef65aae commit 0a040b2
Show file tree
Hide file tree
Showing 16 changed files with 447 additions and 168 deletions.
4 changes: 3 additions & 1 deletion fs/cifs/cifs_dfs_ref.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,9 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
* gives us the latter, so we must adjust the result.
*/
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)
goto cdda_exit;

Expand Down
6 changes: 3 additions & 3 deletions fs/cifs/cifs_unicode.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ wchar_t cifs_toupper(wchar_t in);
* Returns:
* Address of the first string
*/
static inline wchar_t *
UniStrcat(wchar_t *ucs1, const wchar_t *ucs2)
static inline __le16 *
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 */
ucs1--; /* Return to the null */
Expand Down
5 changes: 4 additions & 1 deletion fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,9 @@ struct smb_version_operations {
int (*is_transform_hdr)(void *buf);
int (*receive_transform)(struct TCP_Server_Info *,
struct mid_q_entry **);
enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
enum securityEnum);

};

struct smb_version_values {
Expand Down Expand Up @@ -822,7 +825,7 @@ struct cifs_ses {
int ses_count; /* reference counter */
enum statusEnum status;
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 *serverNOS; /* name of network operating system of server */
char *serverDomain; /* security realm of server */
Expand Down
16 changes: 10 additions & 6 deletions fs/cifs/cifspdu.h
Original file line number Diff line number Diff line change
Expand Up @@ -2086,17 +2086,21 @@ typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */
__u8 ServiceSiteGuid[16]; /* MBZ, ignored */
} __attribute__((packed)) REFERRAL3;

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 {
__le16 PathConsumed;
__le16 NumberOfReferrals;
__le32 DFSFlags;
REFERRAL3 referrals[1]; /* array of level 3 dfs_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 */
#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */
Expand Down
9 changes: 9 additions & 0 deletions fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ extern void exit_cifs_idmap(void);
extern int init_cifs_spnego(void);
extern void exit_cifs_spnego(void);
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,
struct cifs_sb_info *cifs_sb,
struct cifs_tcon *tcon,
Expand Down Expand Up @@ -284,6 +286,11 @@ extern int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_codepage,
unsigned int *num_referrals,
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,
struct cifs_sb_info *cifs_sb,
struct smb_vol *vol);
Expand Down Expand Up @@ -526,4 +533,6 @@ int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
int __cifs_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server, char *signature,
struct shash_desc *shash);
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
enum securityEnum);
#endif /* _CIFSPROTO_H */
119 changes: 5 additions & 114 deletions fs/cifs/cifssmb.c
Original file line number Diff line number Diff line change
Expand Up @@ -4786,117 +4786,6 @@ CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *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 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
CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
const char *search_name, struct dfs_info3_param **target_nodes,
Expand Down Expand Up @@ -4993,9 +4882,11 @@ CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
get_bcc(&pSMBr->hdr), 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, remap,
search_name);
rc = parse_dfs_referrals(&pSMBr->dfs_data,
le16_to_cpu(pSMBr->t2.DataCount),
num_of_nodes, target_nodes, nls_codepage,
remap, search_name,
(pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) != 0);

GetDFSRefExit:
cifs_buf_release(pSMB);
Expand Down
3 changes: 2 additions & 1 deletion fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
* compatible with the given NEGOTIATE request.
*/
if (select_sectype(server, vol->sectype) == Unspecified)
if (server->ops->select_sectype(server, vol->sectype)
== Unspecified)
return false;

/*
Expand Down
13 changes: 12 additions & 1 deletion fs/cifs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
char *
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;
int namelen;
Expand All @@ -92,7 +103,7 @@ build_path_from_dentry(struct dentry *direntry)
unsigned seq;

dirsep = CIFS_DIR_SEP(cifs_sb);
if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
if (prefix)
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
else
dfsplen = 0;
Expand Down
105 changes: 105 additions & 0 deletions fs/cifs/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -640,3 +640,108 @@ cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
cifs_add_pending_open_locked(fid, tlink, open);
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;
}
4 changes: 2 additions & 2 deletions fs/cifs/sess.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
}

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) {
case CIFS_NEGFLAVOR_EXTENDED:
Expand Down Expand Up @@ -1391,7 +1391,7 @@ static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
{
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);
if (type == Unspecified) {
cifs_dbg(VFS,
Expand Down
Loading

0 comments on commit 0a040b2

Please sign in to comment.