]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
cifs: during remount, make sure passwords are in sync
authorShyam Prasad N <sprasad@microsoft.com>
Wed, 30 Oct 2024 06:45:50 +0000 (06:45 +0000)
committerSteve French <stfrench@microsoft.com>
Thu, 28 Nov 2024 15:51:07 +0000 (09:51 -0600)
This fixes scenarios where remount can overwrite the only currently
working password, breaking reconnect.

We recently introduced a password2 field in both ses and ctx structs.
This was done so as to allow the client to rotate passwords for a mount
without any downtime. However, when the client transparently handles
password rotation, it can swap the values of the two password fields
in the ses struct, but not in smb3_fs_context struct that hangs off
cifs_sb. This can lead to a situation where a remount unintentionally
overwrites a working password in the ses struct.

In order to fix this, we first get the passwords in ctx struct
in-sync with ses struct, before replacing them with what the passwords
that could be passed as a part of remount.

Also, in order to avoid race condition between smb2_reconnect and
smb3_reconfigure, we make sure to lock session_mutex before changing
password and password2 fields of the ses structure.

Fixes: 35f834265e0d ("smb3: fix broken reconnect when password changing on the server by allowing password rotation")
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Meetakshi Setiya <msetiya@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/fs_context.c
fs/smb/client/fs_context.h

index c87879e4739b1a48653cb61ec8ea5565f21cec0e..c614c5d8b15e08c3100336d46c745d1b0bc47c73 100644 (file)
@@ -920,12 +920,37 @@ do {                                                                      \
        cifs_sb->ctx->field = NULL;                                     \
 } while (0)
 
+int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
+{
+       if (ses->password &&
+           cifs_sb->ctx->password &&
+           strcmp(ses->password, cifs_sb->ctx->password)) {
+               kfree_sensitive(cifs_sb->ctx->password);
+               cifs_sb->ctx->password = kstrdup(ses->password, GFP_KERNEL);
+               if (!cifs_sb->ctx->password)
+                       return -ENOMEM;
+       }
+       if (ses->password2 &&
+           cifs_sb->ctx->password2 &&
+           strcmp(ses->password2, cifs_sb->ctx->password2)) {
+               kfree_sensitive(cifs_sb->ctx->password2);
+               cifs_sb->ctx->password2 = kstrdup(ses->password2, GFP_KERNEL);
+               if (!cifs_sb->ctx->password2) {
+                       kfree_sensitive(cifs_sb->ctx->password);
+                       cifs_sb->ctx->password = NULL;
+                       return -ENOMEM;
+               }
+       }
+       return 0;
+}
+
 static int smb3_reconfigure(struct fs_context *fc)
 {
        struct smb3_fs_context *ctx = smb3_fc2context(fc);
        struct dentry *root = fc->root;
        struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
        struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
+       char *new_password = NULL, *new_password2 = NULL;
        bool need_recon = false;
        int rc;
 
@@ -945,21 +970,61 @@ static int smb3_reconfigure(struct fs_context *fc)
        STEAL_STRING(cifs_sb, ctx, UNC);
        STEAL_STRING(cifs_sb, ctx, source);
        STEAL_STRING(cifs_sb, ctx, username);
+
        if (need_recon == false)
                STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
        else  {
-               kfree_sensitive(ses->password);
-               ses->password = kstrdup(ctx->password, GFP_KERNEL);
-               if (!ses->password)
-                       return -ENOMEM;
-               kfree_sensitive(ses->password2);
-               ses->password2 = kstrdup(ctx->password2, GFP_KERNEL);
-               if (!ses->password2) {
-                       kfree_sensitive(ses->password);
-                       ses->password = NULL;
+               if (ctx->password) {
+                       new_password = kstrdup(ctx->password, GFP_KERNEL);
+                       if (!new_password)
+                               return -ENOMEM;
+               } else
+                       STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
+       }
+
+       /*
+        * if a new password2 has been specified, then reset it's value
+        * inside the ses struct
+        */
+       if (ctx->password2) {
+               new_password2 = kstrdup(ctx->password2, GFP_KERNEL);
+               if (!new_password2) {
+                       kfree_sensitive(new_password);
                        return -ENOMEM;
                }
+       } else
+               STEAL_STRING_SENSITIVE(cifs_sb, ctx, password2);
+
+       /*
+        * we may update the passwords in the ses struct below. Make sure we do
+        * not race with smb2_reconnect
+        */
+       mutex_lock(&ses->session_mutex);
+
+       /*
+        * smb2_reconnect may swap password and password2 in case session setup
+        * failed. First get ctx passwords in sync with ses passwords. It should
+        * be okay to do this even if this function were to return an error at a
+        * later stage
+        */
+       rc = smb3_sync_session_ctx_passwords(cifs_sb, ses);
+       if (rc)
+               return rc;
+
+       /*
+        * now that allocations for passwords are done, commit them
+        */
+       if (new_password) {
+               kfree_sensitive(ses->password);
+               ses->password = new_password;
        }
+       if (new_password2) {
+               kfree_sensitive(ses->password2);
+               ses->password2 = new_password2;
+       }
+
+       mutex_unlock(&ses->session_mutex);
+
        STEAL_STRING(cifs_sb, ctx, domainname);
        STEAL_STRING(cifs_sb, ctx, nodename);
        STEAL_STRING(cifs_sb, ctx, iocharset);
index 67b7fc48ac583c2b04dbac4d81373af4080004bd..ac6baa774ad3a9c5f26f244be74b983a69001e92 100644 (file)
@@ -309,6 +309,7 @@ static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *f
 }
 
 extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx);
+extern int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses);
 extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb);
 
 /*