Commit ea31a443 authored by J. Bruce Fields's avatar J. Bruce Fields Committed by Trond Myklebust

nfs: Fix misparsing of nfsv4 fs_locations attribute

The code incorrectly assumes here that the server name (or ip address)
is null-terminated.  This can cause referrals to fail in some cases.

Also support ipv6 addresses.
Signed-off-by: default avatarJ. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent f0c92925
......@@ -153,6 +153,7 @@ extern void nfs4_clear_inode(struct inode *);
void nfs_zap_acl_cache(struct inode *inode);
/* super.c */
void nfs_parse_ip_address(char *, size_t, struct sockaddr *, size_t *);
extern struct file_system_type nfs_xdev_fs_type;
#ifdef CONFIG_NFS_V4
extern struct file_system_type nfs4_xdev_fs_type;
......@@ -276,6 +277,7 @@ unsigned int nfs_page_array_len(unsigned int base, size_t len)
PAGE_SIZE - 1) >> PAGE_SHIFT;
}
#define IPV6_SCOPE_DELIMITER '%'
/*
* Set the port number in an address. Be agnostic about the address
......
......@@ -93,50 +93,42 @@ static int nfs4_validate_fspath(const struct vfsmount *mnt_parent,
return 0;
}
/*
* Check if the string represents a "valid" IPv4 address
*/
static inline int valid_ipaddr4(const char *buf)
{
int rc, count, in[4];
rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]);
if (rc != 4)
return -EINVAL;
for (count = 0; count < 4; count++) {
if (in[count] > 255)
return -EINVAL;
}
return 0;
}
static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
char *page, char *page2,
const struct nfs4_fs_location *location)
{
struct vfsmount *mnt = ERR_PTR(-ENOENT);
char *mnt_path;
int page2len;
unsigned int s;
mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE);
if (IS_ERR(mnt_path))
return mnt;
mountdata->mnt_path = mnt_path;
page2 += strlen(mnt_path) + 1;
page2len = PAGE_SIZE - strlen(mnt_path) - 1;
for (s = 0; s < location->nservers; s++) {
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(NFS_PORT),
};
const struct nfs4_string *buf = &location->servers[s];
struct sockaddr_storage addr;
if (location->servers[s].len <= 0 ||
valid_ipaddr4(location->servers[s].data) < 0)
if (buf->len <= 0 || buf->len >= PAGE_SIZE)
continue;
mountdata->hostname = location->servers[s].data;
addr.sin_addr.s_addr = in_aton(mountdata->hostname),
mountdata->addr = (struct sockaddr *)&addr;
mountdata->addrlen = sizeof(addr);
if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
continue;
nfs_parse_ip_address(buf->data, buf->len,
mountdata->addr, &mountdata->addrlen);
if (mountdata->addr->sa_family == AF_UNSPEC)
continue;
nfs_set_port(mountdata->addr, NFS_PORT);
strncpy(page2, buf->data, page2len);
page2[page2len] = '\0';
mountdata->hostname = page2;
snprintf(page, PAGE_SIZE, "%s:%s",
mountdata->hostname,
......
......@@ -716,8 +716,6 @@ static void nfs_parse_ipv4_address(char *string, size_t str_len,
*addr_len = 0;
}
#define IPV6_SCOPE_DELIMITER '%'
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
const char *delim,
......@@ -790,7 +788,7 @@ static void nfs_parse_ipv6_address(char *string, size_t str_len,
* If there is a problem constructing the new sockaddr, set the address
* family to AF_UNSPEC.
*/
static void nfs_parse_ip_address(char *string, size_t str_len,
void nfs_parse_ip_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
unsigned int i, colons;
......
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