Commit c9d75268 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] (4/4) 2.5.5-pre1 fixes

Fixes overflow checks in smb_encode_path().
parent edd97fd7
...@@ -103,6 +103,8 @@ static int convert_memcpy(char *output, int olen, ...@@ -103,6 +103,8 @@ static int convert_memcpy(char *output, int olen,
struct nls_table *nls_from, struct nls_table *nls_from,
struct nls_table *nls_to) struct nls_table *nls_to)
{ {
if (olen < ilen)
return -ENAMETOOLONG;
memcpy(output, input, ilen); memcpy(output, input, ilen);
return ilen; return ilen;
} }
...@@ -126,20 +128,21 @@ static int convert_cp(char *output, int olen, ...@@ -126,20 +128,21 @@ static int convert_cp(char *output, int olen,
/* convert by changing to unicode and back to the new cp */ /* convert by changing to unicode and back to the new cp */
n = nls_from->char2uni((unsigned char *)input, ilen, &ch); n = nls_from->char2uni((unsigned char *)input, ilen, &ch);
if (n < 0) if (n < 0)
goto out; goto fail;
input += n; input += n;
ilen -= n; ilen -= n;
n = nls_to->uni2char(ch, output, olen); n = nls_to->uni2char(ch, output, olen);
if (n < 0) if (n < 0)
goto out; goto fail;
output += n; output += n;
olen -= n; olen -= n;
len += n; len += n;
} }
out:
return len; return len;
fail:
return n;
} }
static int setcodepage(struct nls_table **p, char *name) static int setcodepage(struct nls_table **p, char *name)
...@@ -214,71 +217,79 @@ smb_encode_smb_length(__u8 * p, __u32 len) ...@@ -214,71 +217,79 @@ smb_encode_smb_length(__u8 * p, __u32 len)
* smb_build_path: build the path to entry and name storing it in buf. * smb_build_path: build the path to entry and name storing it in buf.
* The path returned will have the trailing '\0'. * The path returned will have the trailing '\0'.
*/ */
static int smb_build_path(struct smb_sb_info *server, char * buf, static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen,
struct dentry * entry, struct qstr * name) struct dentry * entry, struct qstr * name)
{ {
char *path = buf; char *path = buf;
int len; int len;
if (maxlen < 2)
return -ENAMETOOLONG;
if (maxlen > SMB_MAXNAMELEN + 1)
maxlen = SMB_MAXNAMELEN + 1;
if (entry == NULL) if (entry == NULL)
goto test_name_and_out; goto test_name_and_out;
/* /*
* If IS_ROOT, we have to do no walking at all. * If IS_ROOT, we have to do no walking at all.
*/ */
if (IS_ROOT(entry)) { if (IS_ROOT(entry) && !name) {
*(path++) = '\\'; *path++ = '\\';
if (name != NULL) *path++ = '\0';
goto name_and_out; return 2;
goto out;
} }
/* /*
* Build the path string walking the tree backward from end to ROOT * Build the path string walking the tree backward from end to ROOT
* and store it in reversed order [see reverse_string()] * and store it in reversed order [see reverse_string()]
*/ */
for (;;) { while (!IS_ROOT(entry)) {
if (entry->d_name.len > SMB_MAXNAMELEN) if (maxlen < 3)
return -ENAMETOOLONG;
if (path - buf + entry->d_name.len > SMB_MAXPATHLEN)
return -ENAMETOOLONG; return -ENAMETOOLONG;
len = server->convert(path, SMB_MAXNAMELEN, len = server->convert(path, maxlen-2,
entry->d_name.name, entry->d_name.len, entry->d_name.name, entry->d_name.len,
server->local_nls, server->remote_nls); server->local_nls, server->remote_nls);
if (len < 0)
return len;
reverse_string(path, len); reverse_string(path, len);
path += len; path += len;
*path++ = '\\';
*(path++) = '\\'; maxlen -= len+1;
entry = entry->d_parent; entry = entry->d_parent;
if (IS_ROOT(entry)) if (IS_ROOT(entry))
break; break;
} }
reverse_string(buf, path-buf); reverse_string(buf, path-buf);
/* maxlen is at least 1 */
test_name_and_out: test_name_and_out:
if (name != NULL) { if (name) {
*(path++) = '\\'; if (maxlen < 3)
name_and_out: return -ENAMETOOLONG;
len = server->convert(path, SMB_MAXNAMELEN, *path++ = '\\';
len = server->convert(path, maxlen-2,
name->name, name->len, name->name, name->len,
server->local_nls, server->remote_nls); server->local_nls, server->remote_nls);
if (len < 0)
return len;
path += len; path += len;
maxlen -= len+1;
} }
out: /* maxlen is at least 1 */
*(path++) = '\0'; *path++ = '\0';
return (path-buf); return path-buf;
} }
static int smb_encode_path(struct smb_sb_info *server, char *buf, static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen,
struct dentry *dir, struct qstr *name) struct dentry *dir, struct qstr *name)
{ {
int result; int result;
result = smb_build_path(server, buf, dir, name); result = smb_build_path(server, buf, maxlen, dir, name);
if (result < 0) if (result < 0)
goto out; goto out;
if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS)
...@@ -292,9 +303,12 @@ static int smb_simple_encode_path(struct smb_sb_info *server, char **p, ...@@ -292,9 +303,12 @@ static int smb_simple_encode_path(struct smb_sb_info *server, char **p,
{ {
char *s = *p; char *s = *p;
int res; int res;
int maxlen = ((char *)server->packet + server->packet_size) - s;
if (!maxlen)
return -ENAMETOOLONG;
*s++ = 4; *s++ = 4;
res = smb_encode_path(server, s, entry, name); res = smb_encode_path(server, s, maxlen-1, entry, name);
if (res < 0) if (res < 0)
return res; return res;
*p = s + res; *p = s + res;
...@@ -1588,8 +1602,7 @@ smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir, ...@@ -1588,8 +1602,7 @@ smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
struct smb_sb_info *server = server_from_dentry(dir); struct smb_sb_info *server = server_from_dentry(dir);
struct qstr qname; struct qstr qname;
struct smb_fattr fattr; struct smb_fattr fattr;
char *p;
unsigned char *p;
int result; int result;
int i, first, entries_seen, entries; int i, first, entries_seen, entries;
int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE;
...@@ -1859,7 +1872,7 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir, ...@@ -1859,7 +1872,7 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
*/ */
mask = param + 12; mask = param + 12;
mask_len = smb_encode_path(server, mask, dir, &star); mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dir, &star);
if (mask_len < 0) { if (mask_len < 0) {
result = mask_len; result = mask_len;
goto unlock_return; goto unlock_return;
...@@ -2066,7 +2079,7 @@ smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, ...@@ -2066,7 +2079,7 @@ smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry,
int mask_len, result; int mask_len, result;
retry: retry:
mask_len = smb_encode_path(server, mask, dentry, NULL); mask_len = smb_encode_path(server, mask, SMB_MAXNAMELEN+1, dentry, NULL);
if (mask_len < 0) { if (mask_len < 0) {
result = mask_len; result = mask_len;
goto out; goto out;
...@@ -2192,7 +2205,7 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, ...@@ -2192,7 +2205,7 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
retry: retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0); DSET(param, 2, 0);
result = smb_encode_path(server, param + 6, dir, NULL); result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL);
if (result < 0) if (result < 0)
goto out; goto out;
p = param + 6 + result; p = param + 6 + result;
...@@ -2438,7 +2451,7 @@ smb_proc_setattr_trans2(struct smb_sb_info *server, ...@@ -2438,7 +2451,7 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
retry: retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
DSET(param, 2, 0); DSET(param, 2, 0);
result = smb_encode_path(server, param + 6, dir, NULL); result = smb_encode_path(server, param+6, SMB_MAXNAMELEN+1, dir, NULL);
if (result < 0) if (result < 0)
goto out; goto out;
p = param + 6 + result; p = param + 6 + result;
......
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