smb: client: fix parsing of source mount option
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>
This commit is contained in:
Родитель
d439b29057
Коммит
49024ec879
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче