Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0c4e2ac authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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 */
+10 −6
Original line number Diff line number Diff line
@@ -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);

+13 −1
Original line number Diff line number Diff line
@@ -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;
	}

	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;

+52 −1
Original line number Diff line number Diff line
@@ -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);
}

+39 −5
Original line number Diff line number Diff line
@@ -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
Loading