[linux-cifs-client] Re: [PATCH] cifs: make sure that DFS pathnames are properly formed

Steve French smfrench at gmail.com
Tue Dec 16 03:18:29 GMT 2008


This fix looks correct and urgent, although if we have time I would
like to try it for another day before requesting merge to linux-2.6

The dfs documentation does state that we should be sending \ rather
than \\ (although earlier places in the document stated the reverse,
it is more clear later in the network documentation).
On Mon, Dec 15, 2008 at 7:02 PM, Jeff Layton <jlayton at redhat.com> wrote:
> The paths in a DFS request are supposed to only have a single preceding
> backslash, but we are sending them with a double backslash. This is
> exposing a bug in Windows where it also sends a path in the response
> that has a double backslash.
>
> The existing code that builds the mount option string however expects a
> double backslash prefix in a couple of places when it tries to use the
> path returned by build_path_from_dentry. Fix compose_mount_options to
> expect properly formed DFS paths (single backslash at front).
>
> Also clean up error handling in that function. There was a possible
> NULL pointer dereference and situations where a partially built option
> string would be returned.
>
> Tested against Samba 3.0.28-ish server and Win2k8.
>
> Signed-off-by: Jeff Layton <jlayton at redhat.com>
> ---
>  fs/cifs/cifs_dfs_ref.c |   48 ++++++++++++++++++++++++++++++++++++------------
>  1 files changed, 36 insertions(+), 12 deletions(-)
>
> diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
> index e1c1836..85c0a74 100644
> --- a/fs/cifs/cifs_dfs_ref.c
> +++ b/fs/cifs/cifs_dfs_ref.c
> @@ -122,7 +122,7 @@ static char *compose_mount_options(const char *sb_mountdata,
>                                   char **devname)
>  {
>        int rc;
> -       char *mountdata;
> +       char *mountdata = NULL;
>        int md_len;
>        char *tkn_e;
>        char *srvIP = NULL;
> @@ -136,10 +136,9 @@ static char *compose_mount_options(const char *sb_mountdata,
>        *devname = cifs_get_share_name(ref->node_name);
>        rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
>        if (rc != 0) {
> -               cERROR(1, ("%s: Failed to resolve server part of %s to IP",
> -                         __func__, *devname));
> -               mountdata = ERR_PTR(rc);
> -               goto compose_mount_options_out;
> +               cERROR(1, ("%s: Failed to resolve server part of %s to IP: %d",
> +                         __func__, *devname, rc));;
> +               goto compose_mount_options_err;
>        }
>        /* md_len = strlen(...) + 12 for 'sep+prefixpath='
>         * assuming that we have 'unc=' and 'ip=' in
> @@ -149,8 +148,8 @@ static char *compose_mount_options(const char *sb_mountdata,
>                strlen(ref->node_name) + 12;
>        mountdata = kzalloc(md_len+1, GFP_KERNEL);
>        if (mountdata == NULL) {
> -               mountdata = ERR_PTR(-ENOMEM);
> -               goto compose_mount_options_out;
> +               rc = -ENOMEM;
> +               goto compose_mount_options_err;
>        }
>
>        /* copy all options except of unc,ip,prefixpath */
> @@ -197,18 +196,32 @@ static char *compose_mount_options(const char *sb_mountdata,
>
>        /* find & copy prefixpath */
>        tkn_e = strchr(ref->node_name + 2, '\\');
> -       if (tkn_e == NULL) /* invalid unc, missing share name*/
> -               goto compose_mount_options_out;
> +       if (tkn_e == NULL) {
> +               /* invalid unc, missing share name*/
> +               rc = -EINVAL;
> +               goto compose_mount_options_err;
> +       }
>
> +       /*
> +        * this function gives us a path with a double backslash prefix. We
> +        * require a single backslash for DFS. Temporarily increment fullpath
> +        * to put it in the proper form and decrement before freeing it.
> +        */
>        fullpath = build_path_from_dentry(dentry);
> +       if (!fullpath) {
> +               rc = -ENOMEM;
> +               goto compose_mount_options_err;
> +       }
> +       ++fullpath;
>        tkn_e = strchr(tkn_e + 1, '\\');
> -       if (tkn_e || strlen(fullpath) - (ref->path_consumed)) {
> +       if (tkn_e || (strlen(fullpath) - ref->path_consumed)) {
>                strncat(mountdata, &sep, 1);
>                strcat(mountdata, "prefixpath=");
>                if (tkn_e)
>                        strcat(mountdata, tkn_e + 1);
> -               strcat(mountdata, fullpath + (ref->path_consumed));
> +               strcat(mountdata, fullpath + ref->path_consumed);
>        }
> +       --fullpath;
>        kfree(fullpath);
>
>        /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/
> @@ -217,6 +230,11 @@ static char *compose_mount_options(const char *sb_mountdata,
>  compose_mount_options_out:
>        kfree(srvIP);
>        return mountdata;
> +
> +compose_mount_options_err:
> +       kfree(mountdata);
> +       mountdata = ERR_PTR(rc);
> +       goto compose_mount_options_out;
>  }
>
>
> @@ -309,13 +327,19 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
>                goto out_err;
>        }
>
> +       /*
> +        * The MSDFS spec states that paths in DFS referral requests and
> +        * responses must be prefixed by a single '\' character instead of
> +        * the double backslashes usually used in the UNC. This function
> +        * gives us the latter, so we must adjust the result.
> +        */
>        full_path = build_path_from_dentry(dentry);
>        if (full_path == NULL) {
>                rc = -ENOMEM;
>                goto out_err;
>        }
>
> -       rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls,
> +       rc = get_dfs_path(xid, ses , full_path + 1, cifs_sb->local_nls,
>                &num_referrals, &referrals,
>                cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
>
> --
> 1.5.5.1
>
>



-- 
Thanks,

Steve


More information about the linux-cifs-client mailing list