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
This commit is contained in:
commit
b0c4e2acdd
7 changed files with 169 additions and 16 deletions
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
@ -769,13 +771,13 @@ 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,
|
||||
|
@ -783,7 +785,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
|
|||
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,
|
||||
|
@ -791,6 +793,8 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
|
|||
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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -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);) {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -399,10 +422,14 @@ 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);
|
||||
|
||||
|
@ -410,6 +437,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
|||
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
|
||||
|
|
|
@ -1002,10 +1002,26 @@ 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;
|
||||
|
@ -1013,7 +1029,8 @@ struct inode *cifs_root_iget(struct super_block *sb)
|
|||
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) {
|
||||
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -1044,6 +1044,9 @@ smb2_new_lease_key(struct cifs_fid *fid)
|
|||
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
|
||||
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *full_path, char **target_path,
|
||||
|
@ -1056,7 +1059,10 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
struct cifs_fid fid;
|
||||
struct smb2_err_rsp *err_buf = NULL;
|
||||
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);
|
||||
|
||||
|
@ -1077,11 +1083,33 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
kfree(utf16_path);
|
||||
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 */
|
||||
rc = 0;
|
||||
symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
|
||||
sub_len = le16_to_cpu(symlink->SubstituteNameLength);
|
||||
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(
|
||||
(char *)symlink->PathBuffer + sub_offset,
|
||||
sub_len, true, cifs_sb->local_nls);
|
||||
|
@ -1515,6 +1543,8 @@ struct smb_version_operations smb20_operations = {
|
|||
.rename = smb2_rename_path,
|
||||
.create_hardlink = smb2_create_hardlink,
|
||||
.query_symlink = smb2_query_symlink,
|
||||
.query_mf_symlink = smb3_query_mf_symlink,
|
||||
.create_mf_symlink = smb3_create_mf_symlink,
|
||||
.open = smb2_open_file,
|
||||
.set_fid = smb2_set_fid,
|
||||
.close = smb2_close_file,
|
||||
|
|
Loading…
Reference in a new issue