summaryrefslogtreecommitdiff
path: root/fs/smb/client/connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smb/client/connect.c')
-rw-r--r--fs/smb/client/connect.c86
1 files changed, 75 insertions, 11 deletions
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 0ce2d704b1f3..56b3a9eb9b05 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -1897,11 +1897,35 @@ static int match_session(struct cifs_ses *ses,
CIFS_MAX_USERNAME_LEN))
return 0;
if ((ctx->username && strlen(ctx->username) != 0) &&
- ses->password != NULL &&
- strncmp(ses->password,
- ctx->password ? ctx->password : "",
- CIFS_MAX_PASSWORD_LEN))
- return 0;
+ ses->password != NULL) {
+
+ /* New mount can only share sessions with an existing mount if:
+ * 1. Both password and password2 match, or
+ * 2. password2 of the old mount matches password of the new mount
+ * and password of the old mount matches password2 of the new
+ * mount
+ */
+ if (ses->password2 != NULL && ctx->password2 != NULL) {
+ if (!((strncmp(ses->password, ctx->password ?
+ ctx->password : "", CIFS_MAX_PASSWORD_LEN) == 0 &&
+ strncmp(ses->password2, ctx->password2,
+ CIFS_MAX_PASSWORD_LEN) == 0) ||
+ (strncmp(ses->password, ctx->password2,
+ CIFS_MAX_PASSWORD_LEN) == 0 &&
+ strncmp(ses->password2, ctx->password ?
+ ctx->password : "", CIFS_MAX_PASSWORD_LEN) == 0)))
+ return 0;
+
+ } else if ((ses->password2 == NULL && ctx->password2 != NULL) ||
+ (ses->password2 != NULL && ctx->password2 == NULL)) {
+ return 0;
+
+ } else {
+ if (strncmp(ses->password, ctx->password ?
+ ctx->password : "", CIFS_MAX_PASSWORD_LEN))
+ return 0;
+ }
+ }
}
if (strcmp(ctx->local_nls->charset, ses->local_nls->charset))
@@ -2244,6 +2268,7 @@ struct cifs_ses *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
int rc = 0;
+ int retries = 0;
unsigned int xid;
struct cifs_ses *ses;
struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
@@ -2262,6 +2287,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
cifs_dbg(FYI, "Session needs reconnect\n");
mutex_lock(&ses->session_mutex);
+
+retry_old_session:
rc = cifs_negotiate_protocol(xid, ses, server);
if (rc) {
mutex_unlock(&ses->session_mutex);
@@ -2274,6 +2301,13 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
rc = cifs_setup_session(xid, ses, server,
ctx->local_nls);
if (rc) {
+ if (((rc == -EACCES) || (rc == -EKEYEXPIRED) ||
+ (rc == -EKEYREVOKED)) && !retries && ses->password2) {
+ retries++;
+ cifs_dbg(FYI, "Session reconnect failed, retrying with alternate password\n");
+ swap(ses->password, ses->password2);
+ goto retry_old_session;
+ }
mutex_unlock(&ses->session_mutex);
/* problem -- put our reference */
cifs_put_smb_ses(ses);
@@ -2339,6 +2373,26 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->sectype = ctx->sectype;
ses->sign = ctx->sign;
+
+ /*
+ *Explicitly marking upcall_target mount option for easier handling
+ * by cifs_spnego.c and eventually cifs.upcall.c
+ */
+
+ switch (ctx->upcall_target) {
+ case UPTARGET_UNSPECIFIED: /* default to app */
+ case UPTARGET_APP:
+ ses->upcall_target = UPTARGET_APP;
+ break;
+ case UPTARGET_MOUNT:
+ ses->upcall_target = UPTARGET_MOUNT;
+ break;
+ default:
+ // should never happen
+ ses->upcall_target = UPTARGET_APP;
+ break;
+ }
+
ses->local_nls = load_nls(ctx->local_nls->charset);
/* add server as first channel */
@@ -2349,6 +2403,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->chans_need_reconnect = 1;
spin_unlock(&ses->chan_lock);
+retry_new_session:
mutex_lock(&ses->session_mutex);
rc = cifs_negotiate_protocol(xid, ses, server);
if (!rc)
@@ -2361,8 +2416,16 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
sizeof(ses->smb3signingkey));
spin_unlock(&ses->chan_lock);
- if (rc)
- goto get_ses_fail;
+ if (rc) {
+ if (((rc == -EACCES) || (rc == -EKEYEXPIRED) ||
+ (rc == -EKEYREVOKED)) && !retries && ses->password2) {
+ retries++;
+ cifs_dbg(FYI, "Session setup failed, retrying with alternate password\n");
+ swap(ses->password, ses->password2);
+ goto retry_new_session;
+ } else
+ goto get_ses_fail;
+ }
/*
* success, put it on the list and add it as first channel
@@ -2551,7 +2614,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
if (ses->server->dialect >= SMB20_PROT_ID &&
(ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING))
- nohandlecache = ctx->nohandlecache;
+ nohandlecache = ctx->nohandlecache || !dir_cache_timeout;
else
nohandlecache = true;
tcon = tcon_info_alloc(!nohandlecache, netfs_trace_tcon_ref_new);
@@ -4324,10 +4387,10 @@ cifs_prune_tlinks(struct work_struct *work)
}
#ifndef CONFIG_CIFS_DFS_UPCALL
-int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
+int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon)
{
- int rc;
const struct smb_version_operations *ops = tcon->ses->server->ops;
+ int rc;
/* only send once per connect */
spin_lock(&tcon->tc_lock);
@@ -4350,7 +4413,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
tcon->status = TID_IN_TCON;
spin_unlock(&tcon->tc_lock);
- rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, nlsc);
+ rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name,
+ tcon, tcon->ses->local_nls);
if (rc) {
spin_lock(&tcon->tc_lock);
if (tcon->status == TID_IN_TCON)