Commit b0c4e2ac authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6

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
parents c624c866 7893242e
...@@ -46,6 +46,9 @@ ...@@ -46,6 +46,9 @@
#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */ #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_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_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 cifs_sb_info {
struct rb_root tlink_tree; struct rb_root tlink_tree;
...@@ -67,5 +70,6 @@ struct cifs_sb_info { ...@@ -67,5 +70,6 @@ struct cifs_sb_info {
struct backing_dev_info bdi; struct backing_dev_info bdi;
struct delayed_work prune_tlinks; struct delayed_work prune_tlinks;
struct rcu_head rcu; struct rcu_head rcu;
char *prepath;
}; };
#endif /* _CIFS_FS_SB_H */ #endif /* _CIFS_FS_SB_H */
...@@ -743,24 +743,26 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -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); memcpy(ses->auth_key.response + baselen, tiblob, tilen);
mutex_lock(&ses->server->srv_mutex);
rc = crypto_hmacmd5_alloc(ses->server); rc = crypto_hmacmd5_alloc(ses->server);
if (rc) { if (rc) {
cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc); cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc);
goto setup_ntlmv2_rsp_ret; goto unlock;
} }
/* calculate ntlmv2_hash */ /* calculate ntlmv2_hash */
rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp); rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
if (rc) { if (rc) {
cifs_dbg(VFS, "could not get v2 hash rc %d\n", 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) */ /* calculate first part of the client response (CR1) */
rc = CalcNTLMv2_response(ses, ntlmv2_hash); rc = CalcNTLMv2_response(ses, ntlmv2_hash);
if (rc) { if (rc) {
cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", 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 */ /* now calculate the session key for NTLMv2 */
...@@ -769,13 +771,13 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -769,13 +771,13 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
__func__); __func__);
goto setup_ntlmv2_rsp_ret; goto unlock;
} }
rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); 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, rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
...@@ -783,7 +785,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -783,7 +785,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
CIFS_HMAC_MD5_HASH_SIZE); CIFS_HMAC_MD5_HASH_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__); 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, rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
...@@ -791,6 +793,8 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -791,6 +793,8 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
if (rc) if (rc)
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
unlock:
mutex_unlock(&ses->server->srv_mutex);
setup_ntlmv2_rsp_ret: setup_ntlmv2_rsp_ret:
kfree(tiblob); kfree(tiblob);
......
...@@ -689,6 +689,14 @@ cifs_do_mount(struct file_system_type *fs_type, ...@@ -689,6 +689,14 @@ cifs_do_mount(struct file_system_type *fs_type,
goto out_cifs_sb; 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); cifs_setup_cifs_sb(volume_info, cifs_sb);
rc = cifs_mount(cifs_sb, volume_info); rc = cifs_mount(cifs_sb, volume_info);
...@@ -727,7 +735,11 @@ cifs_do_mount(struct file_system_type *fs_type, ...@@ -727,7 +735,11 @@ cifs_do_mount(struct file_system_type *fs_type,
sb->s_flags |= MS_ACTIVE; 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)) if (IS_ERR(root))
goto out_super; goto out_super;
......
...@@ -1228,6 +1228,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -1228,6 +1228,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->ops = &smb1_operations; vol->ops = &smb1_operations;
vol->vals = &smb1_values; vol->vals = &smb1_values;
vol->echo_interval = SMB_ECHO_INTERVAL_DEFAULT;
if (!mountdata) if (!mountdata)
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
...@@ -2049,7 +2051,7 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol) ...@@ -2049,7 +2051,7 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol)
if (!match_security(server, vol)) if (!match_security(server, vol))
return 0; return 0;
if (server->echo_interval != vol->echo_interval) if (server->echo_interval != vol->echo_interval * HZ)
return 0; return 0;
return 1; return 1;
...@@ -3483,6 +3485,44 @@ cifs_get_volume_info(char *mount_data, const char *devname) ...@@ -3483,6 +3485,44 @@ cifs_get_volume_info(char *mount_data, const char *devname)
return volume_info; 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 int
cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
{ {
...@@ -3620,6 +3660,16 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) ...@@ -3620,6 +3660,16 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
kfree(full_path); kfree(full_path);
goto mount_fail_check; 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); kfree(full_path);
} }
...@@ -3889,6 +3939,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb) ...@@ -3889,6 +3939,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
bdi_destroy(&cifs_sb->bdi); bdi_destroy(&cifs_sb->bdi);
kfree(cifs_sb->mountdata); kfree(cifs_sb->mountdata);
kfree(cifs_sb->prepath);
call_rcu(&cifs_sb->rcu, delayed_free); call_rcu(&cifs_sb->rcu, delayed_free);
} }
......
...@@ -84,6 +84,7 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -84,6 +84,7 @@ build_path_from_dentry(struct dentry *direntry)
struct dentry *temp; struct dentry *temp;
int namelen; int namelen;
int dfsplen; int dfsplen;
int pplen = 0;
char *full_path; char *full_path;
char dirsep; char dirsep;
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
...@@ -95,8 +96,12 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -95,8 +96,12 @@ build_path_from_dentry(struct dentry *direntry)
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1); dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
else else
dfsplen = 0; 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: cifs_bp_rename_retry:
namelen = dfsplen; namelen = dfsplen + pplen;
seq = read_seqbegin(&rename_lock); seq = read_seqbegin(&rename_lock);
rcu_read_lock(); rcu_read_lock();
for (temp = direntry; !IS_ROOT(temp);) { for (temp = direntry; !IS_ROOT(temp);) {
...@@ -137,7 +142,7 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -137,7 +142,7 @@ build_path_from_dentry(struct dentry *direntry)
} }
} }
rcu_read_unlock(); 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", cifs_dbg(FYI, "did not end path lookup where expected. namelen=%ddfsplen=%d\n",
namelen, dfsplen); namelen, dfsplen);
/* presumably this is only possible if racing with a rename /* presumably this is only possible if racing with a rename
...@@ -153,6 +158,17 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -153,6 +158,17 @@ build_path_from_dentry(struct dentry *direntry)
those safely to '/' if any are found in the middle of the prepath */ those safely to '/' if any are found in the middle of the prepath */
/* BB test paths to Windows with '/' in the midst of 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) { if (dfsplen) {
strncpy(full_path, tcon->treeName, dfsplen); strncpy(full_path, tcon->treeName, dfsplen);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
...@@ -229,6 +245,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -229,6 +245,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
goto cifs_create_get_file_info; 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)) { if (!S_ISREG(newinode->i_mode)) {
/* /*
* The server may allow us to open things like * The server may allow us to open things like
...@@ -399,10 +422,14 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -399,10 +422,14 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
if (rc != 0) { if (rc != 0) {
cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n", cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n",
rc); rc);
if (server->ops->close) goto out_err;
server->ops->close(xid, tcon, fid);
goto out;
} }
if (S_ISDIR(newinode->i_mode)) {
rc = -EISDIR;
goto out_err;
}
d_drop(direntry); d_drop(direntry);
d_add(direntry, newinode); d_add(direntry, newinode);
...@@ -410,6 +437,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -410,6 +437,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
kfree(buf); kfree(buf);
kfree(full_path); kfree(full_path);
return rc; return rc;
out_err:
if (server->ops->close)
server->ops->close(xid, tcon, fid);
if (newinode)
iput(newinode);
goto out;
} }
int int
......
...@@ -1002,10 +1002,26 @@ struct inode *cifs_root_iget(struct super_block *sb) ...@@ -1002,10 +1002,26 @@ struct inode *cifs_root_iget(struct super_block *sb)
struct inode *inode = NULL; struct inode *inode = NULL;
long rc; long rc;
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 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(); xid = get_xid();
if (tcon->unix_ext) { 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 */ /* some servers mistakenly claim POSIX support */
if (rc != -EOPNOTSUPP) if (rc != -EOPNOTSUPP)
goto iget_no_retry; goto iget_no_retry;
...@@ -1013,7 +1029,8 @@ struct inode *cifs_root_iget(struct super_block *sb) ...@@ -1013,7 +1029,8 @@ struct inode *cifs_root_iget(struct super_block *sb)
tcon->unix_ext = false; 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: iget_no_retry:
if (!inode) { if (!inode) {
...@@ -1042,6 +1059,7 @@ struct inode *cifs_root_iget(struct super_block *sb) ...@@ -1042,6 +1059,7 @@ struct inode *cifs_root_iget(struct super_block *sb)
} }
out: out:
kfree(path);
/* can not call macro free_xid here since in a void func /* can not call macro free_xid here since in a void func
* TODO: This is no longer true * TODO: This is no longer true
*/ */
......
...@@ -1044,6 +1044,9 @@ smb2_new_lease_key(struct cifs_fid *fid) ...@@ -1044,6 +1044,9 @@ smb2_new_lease_key(struct cifs_fid *fid)
get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE); get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
} }
#define SMB2_SYMLINK_STRUCT_SIZE \
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
static int static int
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
const char *full_path, char **target_path, const char *full_path, char **target_path,
...@@ -1056,7 +1059,10 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1056,7 +1059,10 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid fid; struct cifs_fid fid;
struct smb2_err_rsp *err_buf = NULL; struct smb2_err_rsp *err_buf = NULL;
struct smb2_symlink_err_rsp *symlink; struct smb2_symlink_err_rsp *symlink;
unsigned int sub_len, sub_offset; unsigned int sub_len;
unsigned int sub_offset;
unsigned int print_len;
unsigned int print_offset;
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
...@@ -1077,11 +1083,33 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1077,11 +1083,33 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
kfree(utf16_path); kfree(utf16_path);
return -ENOENT; return -ENOENT;
} }
if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
get_rfc1002_length(err_buf) + 4 < SMB2_SYMLINK_STRUCT_SIZE) {
kfree(utf16_path);
return -ENOENT;
}
/* open must fail on symlink - reset rc */ /* open must fail on symlink - reset rc */
rc = 0; rc = 0;
symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData; symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
sub_len = le16_to_cpu(symlink->SubstituteNameLength); sub_len = le16_to_cpu(symlink->SubstituteNameLength);
sub_offset = le16_to_cpu(symlink->SubstituteNameOffset); sub_offset = le16_to_cpu(symlink->SubstituteNameOffset);
print_len = le16_to_cpu(symlink->PrintNameLength);
print_offset = le16_to_cpu(symlink->PrintNameOffset);
if (get_rfc1002_length(err_buf) + 4 <
SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
kfree(utf16_path);
return -ENOENT;
}
if (get_rfc1002_length(err_buf) + 4 <
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
kfree(utf16_path);
return -ENOENT;
}
*target_path = cifs_strndup_from_utf16( *target_path = cifs_strndup_from_utf16(
(char *)symlink->PathBuffer + sub_offset, (char *)symlink->PathBuffer + sub_offset,
sub_len, true, cifs_sb->local_nls); sub_len, true, cifs_sb->local_nls);
...@@ -1515,6 +1543,8 @@ struct smb_version_operations smb20_operations = { ...@@ -1515,6 +1543,8 @@ struct smb_version_operations smb20_operations = {
.rename = smb2_rename_path, .rename = smb2_rename_path,
.create_hardlink = smb2_create_hardlink, .create_hardlink = smb2_create_hardlink,
.query_symlink = smb2_query_symlink, .query_symlink = smb2_query_symlink,
.query_mf_symlink = smb3_query_mf_symlink,
.create_mf_symlink = smb3_create_mf_symlink,
.open = smb2_open_file, .open = smb2_open_file,
.set_fid = smb2_set_fid, .set_fid = smb2_set_fid,
.close = smb2_close_file, .close = smb2_close_file,
......
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