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 CIFS/SMB3 fixes from Steve French:
 "Various CIFS/SMB3 fixes, most for stable"

* 'for-next' of git://git.samba.org/sfrench/cifs-2.6:
  CIFS: Fix a possible invalid memory access in smb2_query_symlink()
  fs/cifs: make share unaccessible at root level mountable
  cifs: fix crash due to race in hmac(md5) handling
  cifs: unbreak TCP session reuse
  cifs: Check for existing directory when opening file with O_CREAT
  Add MF-Symlinks support for SMB 2.0
  • Loading branch information
torvalds committed Jul 29, 2016
2 parents c624c86 + 7893242 commit b0c4e2a
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 16 deletions.
4 changes: 4 additions & 0 deletions fs/cifs/cifs_fs_sb.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */
#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible
* root mountable
*/

struct cifs_sb_info {
struct rb_root tlink_tree;
Expand All @@ -67,5 +70,6 @@ struct cifs_sb_info {
struct backing_dev_info bdi;
struct delayed_work prune_tlinks;
struct rcu_head rcu;
char *prepath;
};
#endif /* _CIFS_FS_SB_H */
16 changes: 10 additions & 6 deletions fs/cifs/cifsencrypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -743,24 +743,26 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)

memcpy(ses->auth_key.response + baselen, tiblob, tilen);

mutex_lock(&ses->server->srv_mutex);

rc = crypto_hmacmd5_alloc(ses->server);
if (rc) {
cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc);
goto setup_ntlmv2_rsp_ret;
goto unlock;
}

/* calculate ntlmv2_hash */
rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
if (rc) {
cifs_dbg(VFS, "could not get v2 hash rc %d\n", rc);
goto setup_ntlmv2_rsp_ret;
goto unlock;
}

/* calculate first part of the client response (CR1) */
rc = CalcNTLMv2_response(ses, ntlmv2_hash);
if (rc) {
cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc);
goto setup_ntlmv2_rsp_ret;
goto unlock;
}

/* now calculate the session key for NTLMv2 */
Expand All @@ -769,28 +771,30 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
if (rc) {
cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
__func__);
goto setup_ntlmv2_rsp_ret;
goto unlock;
}

rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
if (rc) {
cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__);
goto setup_ntlmv2_rsp_ret;
goto unlock;
}

rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
ntlmv2->ntlmv2_hash,
CIFS_HMAC_MD5_HASH_SIZE);
if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
goto setup_ntlmv2_rsp_ret;
goto unlock;
}

rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response);
if (rc)
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);

unlock:
mutex_unlock(&ses->server->srv_mutex);
setup_ntlmv2_rsp_ret:
kfree(tiblob);

Expand Down
14 changes: 13 additions & 1 deletion fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,14 @@ cifs_do_mount(struct file_system_type *fs_type,
goto out_cifs_sb;
}

if (volume_info->prepath) {
cifs_sb->prepath = kstrdup(volume_info->prepath, GFP_KERNEL);
if (cifs_sb->prepath == NULL) {
root = ERR_PTR(-ENOMEM);
goto out_cifs_sb;
}
}

cifs_setup_cifs_sb(volume_info, cifs_sb);

rc = cifs_mount(cifs_sb, volume_info);
Expand Down Expand Up @@ -727,7 +735,11 @@ cifs_do_mount(struct file_system_type *fs_type,
sb->s_flags |= MS_ACTIVE;
}

root = cifs_get_root(volume_info, sb);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
root = dget(sb->s_root);
else
root = cifs_get_root(volume_info, sb);

if (IS_ERR(root))
goto out_super;

Expand Down
53 changes: 52 additions & 1 deletion fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->ops = &smb1_operations;
vol->vals = &smb1_values;

vol->echo_interval = SMB_ECHO_INTERVAL_DEFAULT;

if (!mountdata)
goto cifs_parse_mount_err;

Expand Down Expand Up @@ -2049,7 +2051,7 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol)
if (!match_security(server, vol))
return 0;

if (server->echo_interval != vol->echo_interval)
if (server->echo_interval != vol->echo_interval * HZ)
return 0;

return 1;
Expand Down Expand Up @@ -3483,6 +3485,44 @@ cifs_get_volume_info(char *mount_data, const char *devname)
return volume_info;
}

static int
cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
char *full_path)
{
int rc;
char *s;
char sep, tmp;

sep = CIFS_DIR_SEP(cifs_sb);
s = full_path;

rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, "");
while (rc == 0) {
/* skip separators */
while (*s == sep)
s++;
if (!*s)
break;
/* next separator */
while (*s && *s != sep)
s++;

/*
* temporarily null-terminate the path at the end of
* the current component
*/
tmp = *s;
*s = 0;
rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
full_path);
*s = tmp;
}
return rc;
}

int
cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
{
Expand Down Expand Up @@ -3620,6 +3660,16 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
kfree(full_path);
goto mount_fail_check;
}

rc = cifs_are_all_path_components_accessible(server,
xid, tcon, cifs_sb,
full_path);
if (rc != 0) {
cifs_dbg(VFS, "cannot query dirs between root and final path, "
"enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
rc = 0;
}
kfree(full_path);
}

Expand Down Expand Up @@ -3889,6 +3939,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb)

bdi_destroy(&cifs_sb->bdi);
kfree(cifs_sb->mountdata);
kfree(cifs_sb->prepath);
call_rcu(&cifs_sb->rcu, delayed_free);
}

Expand Down
44 changes: 39 additions & 5 deletions fs/cifs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ build_path_from_dentry(struct dentry *direntry)
struct dentry *temp;
int namelen;
int dfsplen;
int pplen = 0;
char *full_path;
char dirsep;
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
Expand All @@ -95,8 +96,12 @@ build_path_from_dentry(struct dentry *direntry)
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
else
dfsplen = 0;

if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0;

cifs_bp_rename_retry:
namelen = dfsplen;
namelen = dfsplen + pplen;
seq = read_seqbegin(&rename_lock);
rcu_read_lock();
for (temp = direntry; !IS_ROOT(temp);) {
Expand Down Expand Up @@ -137,7 +142,7 @@ build_path_from_dentry(struct dentry *direntry)
}
}
rcu_read_unlock();
if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
if (namelen != dfsplen + pplen || read_seqretry(&rename_lock, seq)) {
cifs_dbg(FYI, "did not end path lookup where expected. namelen=%ddfsplen=%d\n",
namelen, dfsplen);
/* presumably this is only possible if racing with a rename
Expand All @@ -153,6 +158,17 @@ build_path_from_dentry(struct dentry *direntry)
those safely to '/' if any are found in the middle of the prepath */
/* BB test paths to Windows with '/' in the midst of prepath */

if (pplen) {
int i;

cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath);
memcpy(full_path+dfsplen+1, cifs_sb->prepath, pplen-1);
full_path[dfsplen] = '\\';
for (i = 0; i < pplen-1; i++)
if (full_path[dfsplen+1+i] == '/')
full_path[dfsplen+1+i] = CIFS_DIR_SEP(cifs_sb);
}

if (dfsplen) {
strncpy(full_path, tcon->treeName, dfsplen);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
Expand Down Expand Up @@ -229,6 +245,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
goto cifs_create_get_file_info;
}

if (S_ISDIR(newinode->i_mode)) {
CIFSSMBClose(xid, tcon, fid->netfid);
iput(newinode);
rc = -EISDIR;
goto out;
}

if (!S_ISREG(newinode->i_mode)) {
/*
* The server may allow us to open things like
Expand Down Expand Up @@ -399,17 +422,28 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
if (rc != 0) {
cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n",
rc);
if (server->ops->close)
server->ops->close(xid, tcon, fid);
goto out;
goto out_err;
}

if (S_ISDIR(newinode->i_mode)) {
rc = -EISDIR;
goto out_err;
}

d_drop(direntry);
d_add(direntry, newinode);

out:
kfree(buf);
kfree(full_path);
return rc;

out_err:
if (server->ops->close)
server->ops->close(xid, tcon, fid);
if (newinode)
iput(newinode);
goto out;
}

int
Expand Down
22 changes: 20 additions & 2 deletions fs/cifs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1002,18 +1002,35 @@ struct inode *cifs_root_iget(struct super_block *sb)
struct inode *inode = NULL;
long rc;
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
char *path = NULL;
int len;

if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
&& cifs_sb->prepath) {
len = strlen(cifs_sb->prepath);
path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL);
if (path == NULL)
return ERR_PTR(-ENOMEM);
path[0] = '/';
memcpy(path+1, cifs_sb->prepath, len);
} else {
path = kstrdup("", GFP_KERNEL);
if (path == NULL)
return ERR_PTR(-ENOMEM);
}

xid = get_xid();
if (tcon->unix_ext) {
rc = cifs_get_inode_info_unix(&inode, "", sb, xid);
rc = cifs_get_inode_info_unix(&inode, path, sb, xid);
/* some servers mistakenly claim POSIX support */
if (rc != -EOPNOTSUPP)
goto iget_no_retry;
cifs_dbg(VFS, "server does not support POSIX extensions");
tcon->unix_ext = false;
}

rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL);
convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL);

iget_no_retry:
if (!inode) {
Expand Down Expand Up @@ -1042,6 +1059,7 @@ struct inode *cifs_root_iget(struct super_block *sb)
}

out:
kfree(path);
/* can not call macro free_xid here since in a void func
* TODO: This is no longer true
*/
Expand Down
Loading

0 comments on commit b0c4e2a

Please sign in to comment.