smb: client: fix parsing of source mount option
authorPaulo Alcantara <pc@manguebit.com>
Wed, 28 Jun 2023 00:24:47 +0000 (21:24 -0300)
committerSteve French <stfrench@microsoft.com>
Wed, 28 Jun 2023 16:48:56 +0000 (11:48 -0500)
Handle trailing and leading separators when parsing UNC and prefix
paths in smb3_parse_devname().  Then, store the sanitised paths in
smb3_fs_context::source.

This fixes the following cases

$ mount //srv/share// /mnt/1 -o ...
$ cat /mnt/1/d0/f0
cat: /mnt/1/d0/f0: Invalid argument

The -EINVAL was returned because the client sent SMB2_CREATE "\\d0\f0"
rather than SMB2_CREATE "\d0\f0".

$ mount //srv//share /mnt/1 -o ...
mount: Invalid argument

The -EINVAL was returned correctly although the client only realised
it after sending a couple of bad requests rather than bailing out
earlier when parsing mount options.

Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
Cc: stable@vger.kernel.org
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifs_dfs_ref.c
fs/smb/client/cifsproto.h
fs/smb/client/dfs.c
fs/smb/client/fs_context.c
fs/smb/client/misc.c

index 0329a907bdfe8acc709069d5bb165ddd1fdcdb72..b1c2499b1c3b8b83ada2c28a79c9dfc0c067606c 100644 (file)
@@ -118,12 +118,12 @@ cifs_build_devname(char *nodename, const char *prepath)
        return dev;
 }
 
-static int set_dest_addr(struct smb3_fs_context *ctx, const char *full_path)
+static int set_dest_addr(struct smb3_fs_context *ctx)
 {
        struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
        int rc;
 
-       rc = dns_resolve_server_name_to_ip(full_path, addr, NULL);
+       rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
        if (!rc)
                cifs_set_port(addr, ctx->port);
        return rc;
@@ -171,10 +171,9 @@ static struct vfsmount *cifs_dfs_do_automount(struct path *path)
                mnt = ERR_CAST(full_path);
                goto out;
        }
-       cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
 
        tmp = *cur_ctx;
-       tmp.source = full_path;
+       tmp.source = NULL;
        tmp.leaf_fullpath = NULL;
        tmp.UNC = tmp.prepath = NULL;
        tmp.dfs_root_ses = NULL;
@@ -185,13 +184,22 @@ static struct vfsmount *cifs_dfs_do_automount(struct path *path)
                goto out;
        }
 
-       rc = set_dest_addr(ctx, full_path);
+       rc = smb3_parse_devname(full_path, ctx);
        if (rc) {
                mnt = ERR_PTR(rc);
                goto out;
        }
 
-       rc = smb3_parse_devname(full_path, ctx);
+       ctx->source = smb3_fs_context_fullpath(ctx, '/');
+       if (IS_ERR(ctx->source)) {
+               mnt = ERR_CAST(ctx->source);
+               ctx->source = NULL;
+               goto out;
+       }
+       cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dstaddr=%pISpc\n",
+                __func__, ctx->source, ctx->UNC, ctx->prepath, &ctx->dstaddr);
+
+       rc = set_dest_addr(ctx);
        if (!rc)
                mnt = fc_mount(fc);
        else
index d127aded2f2878e75d0f210eca90de27e6295599..293c54867d9493ba20b53fb543af94cc3bf43d4f 100644 (file)
@@ -85,6 +85,8 @@ extern void release_mid(struct mid_q_entry *mid);
 extern void cifs_wake_up_task(struct mid_q_entry *mid);
 extern int cifs_handle_standard(struct TCP_Server_Info *server,
                                struct mid_q_entry *mid);
+extern char *smb3_fs_context_fullpath(const struct smb3_fs_context *ctx,
+                                     char dirsep);
 extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx);
 extern int smb3_parse_opt(const char *options, const char *key, char **val);
 extern int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs);
index 2390b2fedd6a3292d9e48a600ec4da4166157bba..d741f396c5274167070b7a24912d891f38e4fee0 100644 (file)
@@ -54,39 +54,6 @@ out:
        return rc;
 }
 
-/*
- * cifs_build_path_to_root returns full path to root when we do not have an
- * existing connection (tcon)
- */
-static char *build_unc_path_to_root(const struct smb3_fs_context *ctx,
-                                   const struct cifs_sb_info *cifs_sb, bool useppath)
-{
-       char *full_path, *pos;
-       unsigned int pplen = useppath && ctx->prepath ? strlen(ctx->prepath) + 1 : 0;
-       unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1);
-
-       if (unc_len > MAX_TREE_SIZE)
-               return ERR_PTR(-EINVAL);
-
-       full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL);
-       if (full_path == NULL)
-               return ERR_PTR(-ENOMEM);
-
-       memcpy(full_path, ctx->UNC, unc_len);
-       pos = full_path + unc_len;
-
-       if (pplen) {
-               *pos = CIFS_DIR_SEP(cifs_sb);
-               memcpy(pos + 1, ctx->prepath, pplen);
-               pos += pplen;
-       }
-
-       *pos = '\0'; /* add trailing null */
-       convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
-       cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path);
-       return full_path;
-}
-
 static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
 {
        struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
@@ -179,6 +146,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
        struct TCP_Server_Info *server;
        struct cifs_tcon *tcon;
        char *origin_fullpath = NULL;
+       char sep = CIFS_DIR_SEP(cifs_sb);
        int num_links = 0;
        int rc;
 
@@ -186,7 +154,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
        if (IS_ERR(ref_path))
                return PTR_ERR(ref_path);
 
-       full_path = build_unc_path_to_root(ctx, cifs_sb, true);
+       full_path = smb3_fs_context_fullpath(ctx, sep);
        if (IS_ERR(full_path)) {
                rc = PTR_ERR(full_path);
                full_path = NULL;
@@ -228,7 +196,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
                                kfree(full_path);
                                ref_path = full_path = NULL;
 
-                               full_path = build_unc_path_to_root(ctx, cifs_sb, true);
+                               full_path = smb3_fs_context_fullpath(ctx, sep);
                                if (IS_ERR(full_path)) {
                                        rc = PTR_ERR(full_path);
                                        full_path = NULL;
index 1bda75609b6422ea0a7c7adf0a083094eefaaa5b..4946a0c59600928b9331cca3b392ba6e30a48da6 100644 (file)
@@ -441,14 +441,17 @@ out:
  * but there are some bugs that prevent rename from working if there are
  * multiple delimiters.
  *
- * Returns a sanitized duplicate of @path. @gfp indicates the GFP_* flags
- * for kstrdup.
+ * Return a sanitized duplicate of @path or NULL for empty prefix paths.
+ * Otherwise, return ERR_PTR.
+ *
+ * @gfp indicates the GFP_* flags for kstrdup.
  * The caller is responsible for freeing the original.
  */
 #define IS_DELIM(c) ((c) == '/' || (c) == '\\')
 char *cifs_sanitize_prepath(char *prepath, gfp_t gfp)
 {
        char *cursor1 = prepath, *cursor2 = prepath;
+       char *s;
 
        /* skip all prepended delimiters */
        while (IS_DELIM(*cursor1))
@@ -469,8 +472,39 @@ char *cifs_sanitize_prepath(char *prepath, gfp_t gfp)
        if (IS_DELIM(*(cursor2 - 1)))
                cursor2--;
 
-       *(cursor2) = '\0';
-       return kstrdup(prepath, gfp);
+       *cursor2 = '\0';
+       if (!*prepath)
+               return NULL;
+       s = kstrdup(prepath, gfp);
+       if (!s)
+               return ERR_PTR(-ENOMEM);
+       return s;
+}
+
+/*
+ * Return full path based on the values of @ctx->{UNC,prepath}.
+ *
+ * It is assumed that both values were already parsed by smb3_parse_devname().
+ */
+char *smb3_fs_context_fullpath(const struct smb3_fs_context *ctx, char dirsep)
+{
+       size_t ulen, plen;
+       char *s;
+
+       ulen = strlen(ctx->UNC);
+       plen = ctx->prepath ? strlen(ctx->prepath) + 1 : 0;
+
+       s = kmalloc(ulen + plen + 1, GFP_KERNEL);
+       if (!s)
+               return ERR_PTR(-ENOMEM);
+       memcpy(s, ctx->UNC, ulen);
+       if (plen) {
+               s[ulen] = dirsep;
+               memcpy(s + ulen + 1, ctx->prepath, plen);
+       }
+       s[ulen + plen] = '\0';
+       convert_delimiter(s, dirsep);
+       return s;
 }
 
 /*
@@ -484,6 +518,7 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
        char *pos;
        const char *delims = "/\\";
        size_t len;
+       int rc;
 
        if (unlikely(!devname || !*devname)) {
                cifs_dbg(VFS, "Device name not specified\n");
@@ -511,6 +546,8 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
 
        /* now go until next delimiter or end of string */
        len = strcspn(pos, delims);
+       if (!len)
+               return -EINVAL;
 
        /* move "pos" up to delimiter or NULL */
        pos += len;
@@ -533,8 +570,11 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
                return 0;
 
        ctx->prepath = cifs_sanitize_prepath(pos, GFP_KERNEL);
-       if (!ctx->prepath)
-               return -ENOMEM;
+       if (IS_ERR(ctx->prepath)) {
+               rc = PTR_ERR(ctx->prepath);
+               ctx->prepath = NULL;
+               return rc;
+       }
 
        return 0;
 }
@@ -1146,12 +1186,13 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
                        cifs_errorf(fc, "Unknown error parsing devname\n");
                        goto cifs_parse_mount_err;
                }
-               ctx->source = kstrdup(param->string, GFP_KERNEL);
-               if (ctx->source == NULL) {
+               ctx->source = smb3_fs_context_fullpath(ctx, '/');
+               if (IS_ERR(ctx->source)) {
+                       ctx->source = NULL;
                        cifs_errorf(fc, "OOM when copying UNC string\n");
                        goto cifs_parse_mount_err;
                }
-               fc->source = kstrdup(param->string, GFP_KERNEL);
+               fc->source = kstrdup(ctx->source, GFP_KERNEL);
                if (fc->source == NULL) {
                        cifs_errorf(fc, "OOM when copying UNC string\n");
                        goto cifs_parse_mount_err;
index cd914be905b244d69b560edf1e005e9f255cbb4e..609d0c0d9eca81072ccf80a9b8792763ee17a2a2 100644 (file)
@@ -1198,16 +1198,21 @@ int match_target_ip(struct TCP_Server_Info *server,
 
 int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
 {
+       int rc;
+
        kfree(cifs_sb->prepath);
+       cifs_sb->prepath = NULL;
 
        if (prefix && *prefix) {
                cifs_sb->prepath = cifs_sanitize_prepath(prefix, GFP_ATOMIC);
-               if (!cifs_sb->prepath)
-                       return -ENOMEM;
-
-               convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
-       } else
-               cifs_sb->prepath = NULL;
+               if (IS_ERR(cifs_sb->prepath)) {
+                       rc = PTR_ERR(cifs_sb->prepath);
+                       cifs_sb->prepath = NULL;
+                       return rc;
+               }
+               if (cifs_sb->prepath)
+                       convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
+       }
 
        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
        return 0;