smb3: fix broken reconnect when password changing on the server by allowing password rotation
[ Upstream commit 35f834265e0dc78b003aa0d1af65cafb89666b76 ] There are various use cases that are becoming more common in which password changes are scheduled on a server(s) periodically but the clients connected to this server need to stay connected (even in the face of brief network reconnects) due to mounts which can not be easily unmounted and mounted at will, and servers that do password rotation do not always have the ability to tell the clients exactly when to the new password will be effective, so add support for an alt password ("password2=") on mount (and also remount) so that we can anticipate the upcoming change to the server without risking breaking existing mounts. An alternative would have been to use the kernel keyring for this but the processes doing the reconnect do not have access to the keyring but do have access to the ses structure. Reviewed-by: Bharath SM <bharathsm@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
82f9e213b1
commit
08bedfbc1b
|
@ -1049,6 +1049,7 @@ struct cifs_ses {
|
||||||
and after mount option parsing we fill it */
|
and after mount option parsing we fill it */
|
||||||
char *domainName;
|
char *domainName;
|
||||||
char *password;
|
char *password;
|
||||||
|
char *password2; /* When key rotation used, new password may be set before it expires */
|
||||||
char workstation_name[CIFS_MAX_WORKSTATION_LEN];
|
char workstation_name[CIFS_MAX_WORKSTATION_LEN];
|
||||||
struct session_key auth_key;
|
struct session_key auth_key;
|
||||||
struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
|
struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
|
||||||
|
|
|
@ -2178,6 +2178,7 @@ cifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses)
|
||||||
}
|
}
|
||||||
|
|
||||||
++delim;
|
++delim;
|
||||||
|
/* BB consider adding support for password2 (Key Rotation) for multiuser in future */
|
||||||
ctx->password = kstrndup(delim, len, GFP_KERNEL);
|
ctx->password = kstrndup(delim, len, GFP_KERNEL);
|
||||||
if (!ctx->password) {
|
if (!ctx->password) {
|
||||||
cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n",
|
cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n",
|
||||||
|
@ -2201,6 +2202,7 @@ cifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses)
|
||||||
kfree(ctx->username);
|
kfree(ctx->username);
|
||||||
ctx->username = NULL;
|
ctx->username = NULL;
|
||||||
kfree_sensitive(ctx->password);
|
kfree_sensitive(ctx->password);
|
||||||
|
/* no need to free ctx->password2 since not allocated in this path */
|
||||||
ctx->password = NULL;
|
ctx->password = NULL;
|
||||||
goto out_key_put;
|
goto out_key_put;
|
||||||
}
|
}
|
||||||
|
@ -2312,6 +2314,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
|
||||||
if (!ses->password)
|
if (!ses->password)
|
||||||
goto get_ses_fail;
|
goto get_ses_fail;
|
||||||
}
|
}
|
||||||
|
/* ctx->password freed at unmount */
|
||||||
|
if (ctx->password2) {
|
||||||
|
ses->password2 = kstrdup(ctx->password2, GFP_KERNEL);
|
||||||
|
if (!ses->password2)
|
||||||
|
goto get_ses_fail;
|
||||||
|
}
|
||||||
if (ctx->domainname) {
|
if (ctx->domainname) {
|
||||||
ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL);
|
ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL);
|
||||||
if (!ses->domainName)
|
if (!ses->domainName)
|
||||||
|
|
|
@ -161,6 +161,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
|
||||||
fsparam_string("username", Opt_user),
|
fsparam_string("username", Opt_user),
|
||||||
fsparam_string("pass", Opt_pass),
|
fsparam_string("pass", Opt_pass),
|
||||||
fsparam_string("password", Opt_pass),
|
fsparam_string("password", Opt_pass),
|
||||||
|
fsparam_string("password2", Opt_pass2),
|
||||||
fsparam_string("ip", Opt_ip),
|
fsparam_string("ip", Opt_ip),
|
||||||
fsparam_string("addr", Opt_ip),
|
fsparam_string("addr", Opt_ip),
|
||||||
fsparam_string("domain", Opt_domain),
|
fsparam_string("domain", Opt_domain),
|
||||||
|
@ -314,6 +315,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
|
||||||
new_ctx->nodename = NULL;
|
new_ctx->nodename = NULL;
|
||||||
new_ctx->username = NULL;
|
new_ctx->username = NULL;
|
||||||
new_ctx->password = NULL;
|
new_ctx->password = NULL;
|
||||||
|
new_ctx->password2 = NULL;
|
||||||
new_ctx->server_hostname = NULL;
|
new_ctx->server_hostname = NULL;
|
||||||
new_ctx->domainname = NULL;
|
new_ctx->domainname = NULL;
|
||||||
new_ctx->UNC = NULL;
|
new_ctx->UNC = NULL;
|
||||||
|
@ -326,6 +328,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
|
||||||
DUP_CTX_STR(prepath);
|
DUP_CTX_STR(prepath);
|
||||||
DUP_CTX_STR(username);
|
DUP_CTX_STR(username);
|
||||||
DUP_CTX_STR(password);
|
DUP_CTX_STR(password);
|
||||||
|
DUP_CTX_STR(password2);
|
||||||
DUP_CTX_STR(server_hostname);
|
DUP_CTX_STR(server_hostname);
|
||||||
DUP_CTX_STR(UNC);
|
DUP_CTX_STR(UNC);
|
||||||
DUP_CTX_STR(source);
|
DUP_CTX_STR(source);
|
||||||
|
@ -884,6 +887,8 @@ static int smb3_reconfigure(struct fs_context *fc)
|
||||||
else {
|
else {
|
||||||
kfree_sensitive(ses->password);
|
kfree_sensitive(ses->password);
|
||||||
ses->password = kstrdup(ctx->password, GFP_KERNEL);
|
ses->password = kstrdup(ctx->password, GFP_KERNEL);
|
||||||
|
kfree_sensitive(ses->password2);
|
||||||
|
ses->password2 = kstrdup(ctx->password2, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
STEAL_STRING(cifs_sb, ctx, domainname);
|
STEAL_STRING(cifs_sb, ctx, domainname);
|
||||||
STEAL_STRING(cifs_sb, ctx, nodename);
|
STEAL_STRING(cifs_sb, ctx, nodename);
|
||||||
|
@ -1283,6 +1288,18 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
|
||||||
goto cifs_parse_mount_err;
|
goto cifs_parse_mount_err;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Opt_pass2:
|
||||||
|
kfree_sensitive(ctx->password2);
|
||||||
|
ctx->password2 = NULL;
|
||||||
|
if (strlen(param->string) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ctx->password2 = kstrdup(param->string, GFP_KERNEL);
|
||||||
|
if (ctx->password2 == NULL) {
|
||||||
|
cifs_errorf(fc, "OOM when copying password2 string\n");
|
||||||
|
goto cifs_parse_mount_err;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Opt_ip:
|
case Opt_ip:
|
||||||
if (strlen(param->string) == 0) {
|
if (strlen(param->string) == 0) {
|
||||||
ctx->got_ip = false;
|
ctx->got_ip = false;
|
||||||
|
@ -1582,6 +1599,8 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
|
||||||
cifs_parse_mount_err:
|
cifs_parse_mount_err:
|
||||||
kfree_sensitive(ctx->password);
|
kfree_sensitive(ctx->password);
|
||||||
ctx->password = NULL;
|
ctx->password = NULL;
|
||||||
|
kfree_sensitive(ctx->password2);
|
||||||
|
ctx->password2 = NULL;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1684,6 +1703,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx)
|
||||||
ctx->username = NULL;
|
ctx->username = NULL;
|
||||||
kfree_sensitive(ctx->password);
|
kfree_sensitive(ctx->password);
|
||||||
ctx->password = NULL;
|
ctx->password = NULL;
|
||||||
|
kfree_sensitive(ctx->password2);
|
||||||
|
ctx->password2 = NULL;
|
||||||
kfree(ctx->server_hostname);
|
kfree(ctx->server_hostname);
|
||||||
ctx->server_hostname = NULL;
|
ctx->server_hostname = NULL;
|
||||||
kfree(ctx->UNC);
|
kfree(ctx->UNC);
|
||||||
|
|
|
@ -137,6 +137,7 @@ enum cifs_param {
|
||||||
Opt_source,
|
Opt_source,
|
||||||
Opt_user,
|
Opt_user,
|
||||||
Opt_pass,
|
Opt_pass,
|
||||||
|
Opt_pass2,
|
||||||
Opt_ip,
|
Opt_ip,
|
||||||
Opt_domain,
|
Opt_domain,
|
||||||
Opt_srcaddr,
|
Opt_srcaddr,
|
||||||
|
@ -170,6 +171,7 @@ struct smb3_fs_context {
|
||||||
|
|
||||||
char *username;
|
char *username;
|
||||||
char *password;
|
char *password;
|
||||||
|
char *password2;
|
||||||
char *domainname;
|
char *domainname;
|
||||||
char *source;
|
char *source;
|
||||||
char *server_hostname;
|
char *server_hostname;
|
||||||
|
|
|
@ -101,6 +101,7 @@ sesInfoFree(struct cifs_ses *buf_to_free)
|
||||||
kfree(buf_to_free->serverDomain);
|
kfree(buf_to_free->serverDomain);
|
||||||
kfree(buf_to_free->serverNOS);
|
kfree(buf_to_free->serverNOS);
|
||||||
kfree_sensitive(buf_to_free->password);
|
kfree_sensitive(buf_to_free->password);
|
||||||
|
kfree_sensitive(buf_to_free->password2);
|
||||||
kfree(buf_to_free->user_name);
|
kfree(buf_to_free->user_name);
|
||||||
kfree(buf_to_free->domainName);
|
kfree(buf_to_free->domainName);
|
||||||
kfree_sensitive(buf_to_free->auth_key.response);
|
kfree_sensitive(buf_to_free->auth_key.response);
|
||||||
|
|
|
@ -367,6 +367,17 @@ again:
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = cifs_setup_session(0, ses, server, nls_codepage);
|
rc = cifs_setup_session(0, ses, server, nls_codepage);
|
||||||
|
if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) {
|
||||||
|
/*
|
||||||
|
* Try alternate password for next reconnect (key rotation
|
||||||
|
* could be enabled on the server e.g.) if an alternate
|
||||||
|
* password is available and the current password is expired,
|
||||||
|
* but do not swap on non pwd related errors like host down
|
||||||
|
*/
|
||||||
|
if (ses->password2)
|
||||||
|
swap(ses->password2, ses->password);
|
||||||
|
}
|
||||||
|
|
||||||
if ((rc == -EACCES) && !tcon->retry) {
|
if ((rc == -EACCES) && !tcon->retry) {
|
||||||
mutex_unlock(&ses->session_mutex);
|
mutex_unlock(&ses->session_mutex);
|
||||||
rc = -EHOSTDOWN;
|
rc = -EHOSTDOWN;
|
||||||
|
|
Loading…
Reference in New Issue