]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
smb3: fix broken reconnect when password changing on the server by allowing password...
authorSteve French <stfrench@microsoft.com>
Thu, 4 Apr 2024 23:06:56 +0000 (18:06 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 May 2024 10:02:17 +0000 (12:02 +0200)
[ 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>
fs/smb/client/cifsglob.h
fs/smb/client/connect.c
fs/smb/client/fs_context.c
fs/smb/client/fs_context.h
fs/smb/client/misc.c
fs/smb/client/smb2pdu.c

index 12a48e1d80c3f81ca16423bb2fa4be3a5ff295cd..b598c7ed497bbacb13300955a54d3e51778df33c 100644 (file)
@@ -1049,6 +1049,7 @@ struct cifs_ses {
                                   and after mount option parsing we fill it */
        char *domainName;
        char *password;
+       char *password2; /* When key rotation used, new password may be set before it expires */
        char workstation_name[CIFS_MAX_WORKSTATION_LEN];
        struct session_key auth_key;
        struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
index c5705de7f9de243a9bd72ecf2efd945cc9129c4d..cb3bed8364e07243e7b8e35e61cfcce816cf3790 100644 (file)
@@ -2178,6 +2178,7 @@ cifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses)
        }
 
        ++delim;
+       /* BB consider adding support for password2 (Key Rotation) for multiuser in future */
        ctx->password = kstrndup(delim, len, GFP_KERNEL);
        if (!ctx->password) {
                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);
                        ctx->username = NULL;
                        kfree_sensitive(ctx->password);
+                       /* no need to free ctx->password2 since not allocated in this path */
                        ctx->password = NULL;
                        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)
                        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) {
                ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL);
                if (!ses->domainName)
index 103421791bb5d70cd5afb9ad72ecd548952cd67b..4d9e57be84dbcac20d5264899a17bb9c5969b26f 100644 (file)
@@ -161,6 +161,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
        fsparam_string("username", Opt_user),
        fsparam_string("pass", Opt_pass),
        fsparam_string("password", Opt_pass),
+       fsparam_string("password2", Opt_pass2),
        fsparam_string("ip", Opt_ip),
        fsparam_string("addr", Opt_ip),
        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->username = NULL;
        new_ctx->password = NULL;
+       new_ctx->password2 = NULL;
        new_ctx->server_hostname = NULL;
        new_ctx->domainname = 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(username);
        DUP_CTX_STR(password);
+       DUP_CTX_STR(password2);
        DUP_CTX_STR(server_hostname);
        DUP_CTX_STR(UNC);
        DUP_CTX_STR(source);
@@ -884,6 +887,8 @@ static int smb3_reconfigure(struct fs_context *fc)
        else  {
                kfree_sensitive(ses->password);
                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, nodename);
@@ -1283,6 +1288,18 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
                        goto cifs_parse_mount_err;
                }
                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:
                if (strlen(param->string) == 0) {
                        ctx->got_ip = false;
@@ -1582,6 +1599,8 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
  cifs_parse_mount_err:
        kfree_sensitive(ctx->password);
        ctx->password = NULL;
+       kfree_sensitive(ctx->password2);
+       ctx->password2 = NULL;
        return -EINVAL;
 }
 
@@ -1684,6 +1703,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx)
        ctx->username = NULL;
        kfree_sensitive(ctx->password);
        ctx->password = NULL;
+       kfree_sensitive(ctx->password2);
+       ctx->password2 = NULL;
        kfree(ctx->server_hostname);
        ctx->server_hostname = NULL;
        kfree(ctx->UNC);
index 4e409238fe8f7631bc9ee48804622871dbb4b9e6..d7c090dbe75db8cbc75bd31e9be83b586c5ee629 100644 (file)
@@ -137,6 +137,7 @@ enum cifs_param {
        Opt_source,
        Opt_user,
        Opt_pass,
+       Opt_pass2,
        Opt_ip,
        Opt_domain,
        Opt_srcaddr,
@@ -170,6 +171,7 @@ struct smb3_fs_context {
 
        char *username;
        char *password;
+       char *password2;
        char *domainname;
        char *source;
        char *server_hostname;
index 0d13db80e67c90eda6bdb3c3aab95cc14830656f..d56959d02e36d7ce29a5a8fa728395f820e4f217 100644 (file)
@@ -101,6 +101,7 @@ sesInfoFree(struct cifs_ses *buf_to_free)
        kfree(buf_to_free->serverDomain);
        kfree(buf_to_free->serverNOS);
        kfree_sensitive(buf_to_free->password);
+       kfree_sensitive(buf_to_free->password2);
        kfree(buf_to_free->user_name);
        kfree(buf_to_free->domainName);
        kfree_sensitive(buf_to_free->auth_key.response);
index 94bd4c6d2d682f0fa19daee60868ca35d77cfde6..70530108b9bb9113291bda46b3d59d1f0fc7fdf8 100644 (file)
@@ -367,6 +367,17 @@ again:
                }
 
                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) {
                        mutex_unlock(&ses->session_mutex);
                        rc = -EHOSTDOWN;