1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-30 20:23:49 +03:00

Fix bug #7698 - Assert causes smbd to panic on invalid NetBIOS session request.

Found by the CodeNomicon test suites at the SNIA plugfest.

http://www.codenomicon.com/

If an invalid NetBIOS session request is received the code in name_len() in
libsmb/nmblib.c can hit an assert.

Re-write name_len() and name_extract() to use "buf/len" pairs and
always limit reads.

Jeremy.
This commit is contained in:
Jeremy Allison
2010-09-26 02:59:32 -07:00
parent 80f8419ef2
commit 03841f9e44
6 changed files with 134 additions and 57 deletions

View File

@@ -1241,21 +1241,33 @@ void sort_query_replies(char *data, int n, struct in_addr ip)
/****************************************************************************
Interpret the weird netbios "name" into a unix fstring. Return the name type.
Returns -1 on error.
****************************************************************************/
static int name_interpret(char *in, fstring name)
static int name_interpret(unsigned char *buf, size_t buf_len,
unsigned char *in, fstring name)
{
unsigned char *end_ptr = buf + buf_len;
int ret;
int len = (*in++) / 2;
unsigned int len;
fstring out_string;
char *out = out_string;
unsigned char *out = (unsigned char *)out_string;
*out=0;
if (len > 30 || len<1)
return(0);
if (in >= end_ptr) {
return -1;
}
len = (*in++) / 2;
if (len<1) {
return -1;
}
while (len--) {
if (&in[1] >= end_ptr) {
return -1;
}
if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') {
*out = 0;
return(0);
@@ -1263,21 +1275,13 @@ static int name_interpret(char *in, fstring name)
*out = ((in[0]-'A')<<4) + (in[1]-'A');
in += 2;
out++;
if (PTR_DIFF(out,out_string) >= sizeof(fstring)) {
return -1;
}
}
ret = out[-1];
out[-1] = 0;
#ifdef NETBIOS_SCOPE
/* Handle any scope names */
while(*in) {
*out++ = '.'; /* Scope names are separated by periods */
len = *(unsigned char *)in++;
StrnCpy(out, in, len);
out += len;
*out=0;
in += len;
}
#endif
pull_ascii_fstring(name, out_string);
return(ret);
@@ -1356,12 +1360,25 @@ char *name_mangle(TALLOC_CTX *mem_ctx, char *In, char name_type)
Find a pointer to a netbios name.
****************************************************************************/
static char *name_ptr(char *buf,int ofs)
static unsigned char *name_ptr(unsigned char *buf, size_t buf_len, unsigned int ofs)
{
unsigned char c = *(unsigned char *)(buf+ofs);
unsigned char c = 0;
if (ofs > buf_len || buf_len < 1) {
return NULL;
}
c = *(unsigned char *)(buf+ofs);
if ((c & 0xC0) == 0xC0) {
uint16 l = RSVAL(buf, ofs) & 0x3FFF;
uint16 l = 0;
if (ofs > buf_len - 1) {
return NULL;
}
l = RSVAL(buf, ofs) & 0x3FFF;
if (l > buf_len) {
return NULL;
}
DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l));
return(buf + l);
} else {
@@ -1371,37 +1388,48 @@ static char *name_ptr(char *buf,int ofs)
/****************************************************************************
Extract a netbios name from a buf (into a unix string) return name type.
Returns -1 on error.
****************************************************************************/
int name_extract(char *buf,int ofs, fstring name)
int name_extract(unsigned char *buf, size_t buf_len, unsigned int ofs, fstring name)
{
char *p = name_ptr(buf,ofs);
int d = PTR_DIFF(p,buf+ofs);
unsigned char *p = name_ptr(buf,buf_len,ofs);
name[0] = '\0';
if (d < -50 || d > 50)
return(0);
return(name_interpret(p,name));
if (p == NULL) {
return -1;
}
return(name_interpret(buf,buf_len,p,name));
}
/****************************************************************************
Return the total storage length of a mangled name.
Returns -1 on error.
****************************************************************************/
int name_len(char *s1)
int name_len(unsigned char *s1, size_t buf_len)
{
/* NOTE: this argument _must_ be unsigned */
unsigned char *s = (unsigned char *)s1;
int len;
int len = 0;
if (buf_len < 1) {
return -1;
}
/* If the two high bits of the byte are set, return 2. */
if (0xC0 == (*s & 0xC0))
if (0xC0 == (*s & 0xC0)) {
if (buf_len < 2) {
return -1;
}
return(2);
}
/* Add up the length bytes. */
for (len = 1; (*s); s += (*s) + 1) {
len += *s + 1;
SMB_ASSERT(len < 80);
if (len > buf_len) {
return -1;
}
}
return(len);