Commit c6fbba05 authored by Steve French's avatar Steve French

[CIFS] make sure that DFS pathnames are properly formed

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 Samba 3.3 and Win2k8.

CC: Stable <stable@kernel.org>
Signed-off-by: default avatarJeff Layton <jlayton@redhat.com>
Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent ac6a3ef4
...@@ -4,7 +4,8 @@ Add "forcemandatorylock" mount option to allow user to use mandatory ...@@ -4,7 +4,8 @@ Add "forcemandatorylock" mount option to allow user to use mandatory
rather than posix (advisory) byte range locks, even though server would rather than posix (advisory) byte range locks, even though server would
support posix byte range locks. Fix query of root inode when prefixpath support posix byte range locks. Fix query of root inode when prefixpath
specified and user does not have access to query information about the specified and user does not have access to query information about the
top of the share. top of the share. Fix problem in 2.6.28 resolving DFS paths to
Samba servers (worked to Windows).
Version 1.55 Version 1.55
------------ ------------
......
...@@ -122,7 +122,7 @@ static char *compose_mount_options(const char *sb_mountdata, ...@@ -122,7 +122,7 @@ static char *compose_mount_options(const char *sb_mountdata,
char **devname) char **devname)
{ {
int rc; int rc;
char *mountdata; char *mountdata = NULL;
int md_len; int md_len;
char *tkn_e; char *tkn_e;
char *srvIP = NULL; char *srvIP = NULL;
...@@ -136,10 +136,9 @@ static char *compose_mount_options(const char *sb_mountdata, ...@@ -136,10 +136,9 @@ static char *compose_mount_options(const char *sb_mountdata,
*devname = cifs_get_share_name(ref->node_name); *devname = cifs_get_share_name(ref->node_name);
rc = dns_resolve_server_name_to_ip(*devname, &srvIP); rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
if (rc != 0) { if (rc != 0) {
cERROR(1, ("%s: Failed to resolve server part of %s to IP", cERROR(1, ("%s: Failed to resolve server part of %s to IP: %d",
__func__, *devname)); __func__, *devname, rc));;
mountdata = ERR_PTR(rc); goto compose_mount_options_err;
goto compose_mount_options_out;
} }
/* md_len = strlen(...) + 12 for 'sep+prefixpath=' /* md_len = strlen(...) + 12 for 'sep+prefixpath='
* assuming that we have 'unc=' and 'ip=' in * assuming that we have 'unc=' and 'ip=' in
...@@ -149,8 +148,8 @@ static char *compose_mount_options(const char *sb_mountdata, ...@@ -149,8 +148,8 @@ static char *compose_mount_options(const char *sb_mountdata,
strlen(ref->node_name) + 12; strlen(ref->node_name) + 12;
mountdata = kzalloc(md_len+1, GFP_KERNEL); mountdata = kzalloc(md_len+1, GFP_KERNEL);
if (mountdata == NULL) { if (mountdata == NULL) {
mountdata = ERR_PTR(-ENOMEM); rc = -ENOMEM;
goto compose_mount_options_out; goto compose_mount_options_err;
} }
/* copy all options except of unc,ip,prefixpath */ /* copy all options except of unc,ip,prefixpath */
...@@ -197,18 +196,32 @@ static char *compose_mount_options(const char *sb_mountdata, ...@@ -197,18 +196,32 @@ static char *compose_mount_options(const char *sb_mountdata,
/* find & copy prefixpath */ /* find & copy prefixpath */
tkn_e = strchr(ref->node_name + 2, '\\'); tkn_e = strchr(ref->node_name + 2, '\\');
if (tkn_e == NULL) /* invalid unc, missing share name*/ if (tkn_e == NULL) {
goto compose_mount_options_out; /* 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); fullpath = build_path_from_dentry(dentry);
if (!fullpath) {
rc = -ENOMEM;
goto compose_mount_options_err;
}
++fullpath;
tkn_e = strchr(tkn_e + 1, '\\'); 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); strncat(mountdata, &sep, 1);
strcat(mountdata, "prefixpath="); strcat(mountdata, "prefixpath=");
if (tkn_e) if (tkn_e)
strcat(mountdata, tkn_e + 1); strcat(mountdata, tkn_e + 1);
strcat(mountdata, fullpath + (ref->path_consumed)); strcat(mountdata, fullpath + ref->path_consumed);
} }
--fullpath;
kfree(fullpath); kfree(fullpath);
/*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/ /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/
...@@ -217,6 +230,11 @@ static char *compose_mount_options(const char *sb_mountdata, ...@@ -217,6 +230,11 @@ static char *compose_mount_options(const char *sb_mountdata,
compose_mount_options_out: compose_mount_options_out:
kfree(srvIP); kfree(srvIP);
return mountdata; 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) ...@@ -309,13 +327,19 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
goto out_err; 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); full_path = build_path_from_dentry(dentry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
goto out_err; 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, &num_referrals, &referrals,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment