Skip to content

Commit 49024ec

Browse files
Paulo Alcantarasmfrench
authored andcommitted
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) <[email protected]> Cc: [email protected] Signed-off-by: Steve French <[email protected]>
1 parent d439b29 commit 49024ec

File tree

5 files changed

+80
-56
lines changed

5 files changed

+80
-56
lines changed

fs/smb/client/cifs_dfs_ref.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ cifs_build_devname(char *nodename, const char *prepath)
118118
return dev;
119119
}
120120

121-
static int set_dest_addr(struct smb3_fs_context *ctx, const char *full_path)
121+
static int set_dest_addr(struct smb3_fs_context *ctx)
122122
{
123123
struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
124124
int rc;
125125

126-
rc = dns_resolve_server_name_to_ip(full_path, addr, NULL);
126+
rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
127127
if (!rc)
128128
cifs_set_port(addr, ctx->port);
129129
return rc;
@@ -171,10 +171,9 @@ static struct vfsmount *cifs_dfs_do_automount(struct path *path)
171171
mnt = ERR_CAST(full_path);
172172
goto out;
173173
}
174-
cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
175174

176175
tmp = *cur_ctx;
177-
tmp.source = full_path;
176+
tmp.source = NULL;
178177
tmp.leaf_fullpath = NULL;
179178
tmp.UNC = tmp.prepath = NULL;
180179
tmp.dfs_root_ses = NULL;
@@ -185,13 +184,22 @@ static struct vfsmount *cifs_dfs_do_automount(struct path *path)
185184
goto out;
186185
}
187186

188-
rc = set_dest_addr(ctx, full_path);
187+
rc = smb3_parse_devname(full_path, ctx);
189188
if (rc) {
190189
mnt = ERR_PTR(rc);
191190
goto out;
192191
}
193192

194-
rc = smb3_parse_devname(full_path, ctx);
193+
ctx->source = smb3_fs_context_fullpath(ctx, '/');
194+
if (IS_ERR(ctx->source)) {
195+
mnt = ERR_CAST(ctx->source);
196+
ctx->source = NULL;
197+
goto out;
198+
}
199+
cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dstaddr=%pISpc\n",
200+
__func__, ctx->source, ctx->UNC, ctx->prepath, &ctx->dstaddr);
201+
202+
rc = set_dest_addr(ctx);
195203
if (!rc)
196204
mnt = fc_mount(fc);
197205
else

fs/smb/client/cifsproto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ extern void release_mid(struct mid_q_entry *mid);
8585
extern void cifs_wake_up_task(struct mid_q_entry *mid);
8686
extern int cifs_handle_standard(struct TCP_Server_Info *server,
8787
struct mid_q_entry *mid);
88+
extern char *smb3_fs_context_fullpath(const struct smb3_fs_context *ctx,
89+
char dirsep);
8890
extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx);
8991
extern int smb3_parse_opt(const char *options, const char *key, char **val);
9092
extern int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs);

fs/smb/client/dfs.c

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -54,39 +54,6 @@ int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_para
5454
return rc;
5555
}
5656

57-
/*
58-
* cifs_build_path_to_root returns full path to root when we do not have an
59-
* existing connection (tcon)
60-
*/
61-
static char *build_unc_path_to_root(const struct smb3_fs_context *ctx,
62-
const struct cifs_sb_info *cifs_sb, bool useppath)
63-
{
64-
char *full_path, *pos;
65-
unsigned int pplen = useppath && ctx->prepath ? strlen(ctx->prepath) + 1 : 0;
66-
unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1);
67-
68-
if (unc_len > MAX_TREE_SIZE)
69-
return ERR_PTR(-EINVAL);
70-
71-
full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL);
72-
if (full_path == NULL)
73-
return ERR_PTR(-ENOMEM);
74-
75-
memcpy(full_path, ctx->UNC, unc_len);
76-
pos = full_path + unc_len;
77-
78-
if (pplen) {
79-
*pos = CIFS_DIR_SEP(cifs_sb);
80-
memcpy(pos + 1, ctx->prepath, pplen);
81-
pos += pplen;
82-
}
83-
84-
*pos = '\0'; /* add trailing null */
85-
convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
86-
cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path);
87-
return full_path;
88-
}
89-
9057
static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
9158
{
9259
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
@@ -179,14 +146,15 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
179146
struct TCP_Server_Info *server;
180147
struct cifs_tcon *tcon;
181148
char *origin_fullpath = NULL;
149+
char sep = CIFS_DIR_SEP(cifs_sb);
182150
int num_links = 0;
183151
int rc;
184152

185153
ref_path = dfs_get_path(cifs_sb, ctx->UNC);
186154
if (IS_ERR(ref_path))
187155
return PTR_ERR(ref_path);
188156

189-
full_path = build_unc_path_to_root(ctx, cifs_sb, true);
157+
full_path = smb3_fs_context_fullpath(ctx, sep);
190158
if (IS_ERR(full_path)) {
191159
rc = PTR_ERR(full_path);
192160
full_path = NULL;
@@ -228,7 +196,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
228196
kfree(full_path);
229197
ref_path = full_path = NULL;
230198

231-
full_path = build_unc_path_to_root(ctx, cifs_sb, true);
199+
full_path = smb3_fs_context_fullpath(ctx, sep);
232200
if (IS_ERR(full_path)) {
233201
rc = PTR_ERR(full_path);
234202
full_path = NULL;

fs/smb/client/fs_context.c

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -441,14 +441,17 @@ int smb3_parse_opt(const char *options, const char *key, char **val)
441441
* but there are some bugs that prevent rename from working if there are
442442
* multiple delimiters.
443443
*
444-
* Returns a sanitized duplicate of @path. @gfp indicates the GFP_* flags
445-
* for kstrdup.
444+
* Return a sanitized duplicate of @path or NULL for empty prefix paths.
445+
* Otherwise, return ERR_PTR.
446+
*
447+
* @gfp indicates the GFP_* flags for kstrdup.
446448
* The caller is responsible for freeing the original.
447449
*/
448450
#define IS_DELIM(c) ((c) == '/' || (c) == '\\')
449451
char *cifs_sanitize_prepath(char *prepath, gfp_t gfp)
450452
{
451453
char *cursor1 = prepath, *cursor2 = prepath;
454+
char *s;
452455

453456
/* skip all prepended delimiters */
454457
while (IS_DELIM(*cursor1))
@@ -469,8 +472,39 @@ char *cifs_sanitize_prepath(char *prepath, gfp_t gfp)
469472
if (IS_DELIM(*(cursor2 - 1)))
470473
cursor2--;
471474

472-
*(cursor2) = '\0';
473-
return kstrdup(prepath, gfp);
475+
*cursor2 = '\0';
476+
if (!*prepath)
477+
return NULL;
478+
s = kstrdup(prepath, gfp);
479+
if (!s)
480+
return ERR_PTR(-ENOMEM);
481+
return s;
482+
}
483+
484+
/*
485+
* Return full path based on the values of @ctx->{UNC,prepath}.
486+
*
487+
* It is assumed that both values were already parsed by smb3_parse_devname().
488+
*/
489+
char *smb3_fs_context_fullpath(const struct smb3_fs_context *ctx, char dirsep)
490+
{
491+
size_t ulen, plen;
492+
char *s;
493+
494+
ulen = strlen(ctx->UNC);
495+
plen = ctx->prepath ? strlen(ctx->prepath) + 1 : 0;
496+
497+
s = kmalloc(ulen + plen + 1, GFP_KERNEL);
498+
if (!s)
499+
return ERR_PTR(-ENOMEM);
500+
memcpy(s, ctx->UNC, ulen);
501+
if (plen) {
502+
s[ulen] = dirsep;
503+
memcpy(s + ulen + 1, ctx->prepath, plen);
504+
}
505+
s[ulen + plen] = '\0';
506+
convert_delimiter(s, dirsep);
507+
return s;
474508
}
475509

476510
/*
@@ -484,6 +518,7 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
484518
char *pos;
485519
const char *delims = "/\\";
486520
size_t len;
521+
int rc;
487522

488523
if (unlikely(!devname || !*devname)) {
489524
cifs_dbg(VFS, "Device name not specified\n");
@@ -511,6 +546,8 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
511546

512547
/* now go until next delimiter or end of string */
513548
len = strcspn(pos, delims);
549+
if (!len)
550+
return -EINVAL;
514551

515552
/* move "pos" up to delimiter or NULL */
516553
pos += len;
@@ -533,8 +570,11 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
533570
return 0;
534571

535572
ctx->prepath = cifs_sanitize_prepath(pos, GFP_KERNEL);
536-
if (!ctx->prepath)
537-
return -ENOMEM;
573+
if (IS_ERR(ctx->prepath)) {
574+
rc = PTR_ERR(ctx->prepath);
575+
ctx->prepath = NULL;
576+
return rc;
577+
}
538578

539579
return 0;
540580
}
@@ -1146,12 +1186,13 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
11461186
cifs_errorf(fc, "Unknown error parsing devname\n");
11471187
goto cifs_parse_mount_err;
11481188
}
1149-
ctx->source = kstrdup(param->string, GFP_KERNEL);
1150-
if (ctx->source == NULL) {
1189+
ctx->source = smb3_fs_context_fullpath(ctx, '/');
1190+
if (IS_ERR(ctx->source)) {
1191+
ctx->source = NULL;
11511192
cifs_errorf(fc, "OOM when copying UNC string\n");
11521193
goto cifs_parse_mount_err;
11531194
}
1154-
fc->source = kstrdup(param->string, GFP_KERNEL);
1195+
fc->source = kstrdup(ctx->source, GFP_KERNEL);
11551196
if (fc->source == NULL) {
11561197
cifs_errorf(fc, "OOM when copying UNC string\n");
11571198
goto cifs_parse_mount_err;

fs/smb/client/misc.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,16 +1198,21 @@ int match_target_ip(struct TCP_Server_Info *server,
11981198

11991199
int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
12001200
{
1201+
int rc;
1202+
12011203
kfree(cifs_sb->prepath);
1204+
cifs_sb->prepath = NULL;
12021205

12031206
if (prefix && *prefix) {
12041207
cifs_sb->prepath = cifs_sanitize_prepath(prefix, GFP_ATOMIC);
1205-
if (!cifs_sb->prepath)
1206-
return -ENOMEM;
1207-
1208-
convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
1209-
} else
1210-
cifs_sb->prepath = NULL;
1208+
if (IS_ERR(cifs_sb->prepath)) {
1209+
rc = PTR_ERR(cifs_sb->prepath);
1210+
cifs_sb->prepath = NULL;
1211+
return rc;
1212+
}
1213+
if (cifs_sb->prepath)
1214+
convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
1215+
}
12111216

12121217
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
12131218
return 0;

0 commit comments

Comments
 (0)