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

Merge tag '6.18-rc3-smb-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - fix potential UAF in statfs

 - DFS fix for expired referrals

 - fix minor modinfo typo

 - small improvement to reconnect for smbdirect

* tag '6.18-rc3-smb-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: call smbd_destroy() in the same splace as kernel_sock_shutdown()/sock_release()
  smb: client: handle lack of IPC in dfs_cache_refresh()
  smb: client: fix potential cfid UAF in smb2_query_info_compound
  cifs: fix typo in enable_gcm_256 module parameter
parents 58fdd848 895ad6f7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -173,7 +173,7 @@ module_param(enable_oplocks, bool, 0644);
MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1");

module_param(enable_gcm_256, bool, 0644);
MODULE_PARM_DESC(enable_gcm_256, "Enable requesting strongest (256 bit) GCM encryption. Default: y/Y/0");
MODULE_PARM_DESC(enable_gcm_256, "Enable requesting strongest (256 bit) GCM encryption. Default: y/Y/1");

module_param(require_gcm_256, bool, 0644);
MODULE_PARM_DESC(require_gcm_256, "Require strongest (256 bit) GCM encryption. Default: n/N/0");
+2 −0
Original line number Diff line number Diff line
@@ -616,6 +616,8 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
extern struct TCP_Server_Info *
cifs_find_tcp_session(struct smb3_fs_context *ctx);

struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal);

void __cifs_put_smb_ses(struct cifs_ses *ses);

extern struct cifs_ses *
+19 −27
Original line number Diff line number Diff line
@@ -310,6 +310,8 @@ cifs_abort_connection(struct TCP_Server_Info *server)
			 server->ssocket->flags);
		sock_release(server->ssocket);
		server->ssocket = NULL;
	} else if (cifs_rdma_enabled(server)) {
		smbd_destroy(server);
	}
	server->sequence_number = 0;
	server->session_estab = false;
@@ -338,12 +340,6 @@ cifs_abort_connection(struct TCP_Server_Info *server)
		mid_execute_callback(mid);
		release_mid(mid);
	}

	if (cifs_rdma_enabled(server)) {
		cifs_server_lock(server);
		smbd_destroy(server);
		cifs_server_unlock(server);
	}
}

static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets)
@@ -2015,39 +2011,31 @@ static int match_session(struct cifs_ses *ses,
/**
 * cifs_setup_ipc - helper to setup the IPC tcon for the session
 * @ses: smb session to issue the request on
 * @ctx: the superblock configuration context to use for building the
 *       new tree connection for the IPC (interprocess communication RPC)
 * @seal: if encryption is requested
 *
 * A new IPC connection is made and stored in the session
 * tcon_ipc. The IPC tcon has the same lifetime as the session.
 */
static int
cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal)
{
	int rc = 0, xid;
	struct cifs_tcon *tcon;
	char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
	bool seal = false;
	struct TCP_Server_Info *server = ses->server;

	/*
	 * If the mount request that resulted in the creation of the
	 * session requires encryption, force IPC to be encrypted too.
	 */
	if (ctx->seal) {
		if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)
			seal = true;
		else {
			cifs_server_dbg(VFS,
				 "IPC: server doesn't support encryption\n");
			return -EOPNOTSUPP;
		}
	if (seal && !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) {
		cifs_server_dbg(VFS, "IPC: server doesn't support encryption\n");
		return ERR_PTR(-EOPNOTSUPP);
	}

	/* no need to setup directory caching on IPC share, so pass in false */
	tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_ipc);
	if (tcon == NULL)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);

	spin_lock(&server->srv_lock);
	scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
@@ -2057,13 +2045,13 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
	tcon->ses = ses;
	tcon->ipc = true;
	tcon->seal = seal;
	rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls);
	rc = server->ops->tree_connect(xid, ses, unc, tcon, ses->local_nls);
	free_xid(xid);

	if (rc) {
		cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
		cifs_server_dbg(VFS | ONCE, "failed to connect to IPC (rc=%d)\n", rc);
		tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc_fail);
		goto out;
		return ERR_PTR(rc);
	}

	cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid);
@@ -2071,9 +2059,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
	spin_lock(&tcon->tc_lock);
	tcon->status = TID_GOOD;
	spin_unlock(&tcon->tc_lock);
	ses->tcon_ipc = tcon;
out:
	return rc;
	return tcon;
}

static struct cifs_ses *
@@ -2347,6 +2333,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
	struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
	struct cifs_tcon *ipc;
	struct cifs_ses *ses;
	unsigned int xid;
	int retries = 0;
@@ -2525,7 +2512,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
	list_add(&ses->smb_ses_list, &server->smb_ses_list);
	spin_unlock(&cifs_tcp_ses_lock);

	cifs_setup_ipc(ses, ctx);
	ipc = cifs_setup_ipc(ses, ctx->seal);
	spin_lock(&cifs_tcp_ses_lock);
	spin_lock(&ses->ses_lock);
	ses->tcon_ipc = !IS_ERR(ipc) ? ipc : NULL;
	spin_unlock(&ses->ses_lock);
	spin_unlock(&cifs_tcp_ses_lock);

	free_xid(xid);

+47 −8
Original line number Diff line number Diff line
@@ -1120,24 +1120,63 @@ static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)
	return match;
}

static bool is_ses_good(struct cifs_ses *ses)
static bool is_ses_good(struct cifs_tcon *tcon, struct cifs_ses *ses)
{
	struct TCP_Server_Info *server = ses->server;
	struct cifs_tcon *tcon = ses->tcon_ipc;
	struct cifs_tcon *ipc = NULL;
	bool ret;

	spin_lock(&cifs_tcp_ses_lock);
	spin_lock(&ses->ses_lock);
	spin_lock(&ses->chan_lock);

	ret = !cifs_chan_needs_reconnect(ses, server) &&
		ses->ses_status == SES_GOOD &&
		!tcon->need_reconnect;
		ses->ses_status == SES_GOOD;

	spin_unlock(&ses->chan_lock);

	if (!ret)
		goto out;

	if (likely(ses->tcon_ipc)) {
		if (ses->tcon_ipc->need_reconnect) {
			ret = false;
			goto out;
		}
	} else {
		spin_unlock(&ses->ses_lock);
		spin_unlock(&cifs_tcp_ses_lock);

		ipc = cifs_setup_ipc(ses, tcon->seal);

		spin_lock(&cifs_tcp_ses_lock);
		spin_lock(&ses->ses_lock);
		if (!IS_ERR(ipc)) {
			if (!ses->tcon_ipc) {
				ses->tcon_ipc = ipc;
				ipc = NULL;
			}
		} else {
			ret = false;
			ipc = NULL;
		}
	}

out:
	spin_unlock(&ses->ses_lock);
	spin_unlock(&cifs_tcp_ses_lock);
	if (ipc && server->ops->tree_disconnect) {
		unsigned int xid = get_xid();

		(void)server->ops->tree_disconnect(xid, ipc);
		_free_xid(xid);
	}
	tconInfoFree(ipc, netfs_trace_tcon_ref_free_ipc);
	return ret;
}

/* Refresh dfs referral of @ses */
static void refresh_ses_referral(struct cifs_ses *ses)
static void refresh_ses_referral(struct cifs_tcon *tcon, struct cifs_ses *ses)
{
	struct cache_entry *ce;
	unsigned int xid;
@@ -1153,7 +1192,7 @@ static void refresh_ses_referral(struct cifs_ses *ses)
	}

	ses = CIFS_DFS_ROOT_SES(ses);
	if (!is_ses_good(ses)) {
	if (!is_ses_good(tcon, ses)) {
		cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
			 __func__);
		goto out;
@@ -1241,7 +1280,7 @@ static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh)
	up_read(&htable_rw_lock);

	ses = CIFS_DFS_ROOT_SES(ses);
	if (!is_ses_good(ses)) {
	if (!is_ses_good(tcon, ses)) {
		cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
			 __func__);
		goto out;
@@ -1309,7 +1348,7 @@ void dfs_cache_refresh(struct work_struct *work)
	tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work);

	list_for_each_entry(ses, &tcon->dfs_ses_list, dlist)
		refresh_ses_referral(ses);
		refresh_ses_referral(tcon, ses);
	refresh_tcon_referral(tcon, false);

	queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
+2 −1
Original line number Diff line number Diff line
@@ -2799,11 +2799,12 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
	struct cifs_fid fid;
	int rc;
	__le16 *utf16_path;
	struct cached_fid *cfid = NULL;
	struct cached_fid *cfid;
	int retries = 0, cur_sleep = 1;

replay_again:
	/* reinitialize for possible replay */
	cfid = NULL;
	flags = CIFS_CP_CREATE_CLOSE_OP;
	oplock = SMB2_OPLOCK_LEVEL_NONE;
	server = cifs_pick_channel(ses);