Merge pull request #585 in FS/freeswitch from ~LEKENSTEYN/freeswitch:xml-fixes to master

* commit '48d6a5f6a8fb21a7e20302cfdd4ff012288b3892':
  switch_xml_decode: avoid NUL injection
  switch_xml_set_attr: fix inconsistent state on error paths
This commit is contained in:
Anthony Minessale II 2015-12-10 18:23:48 -06:00
commit cae255395f
1 changed files with 88 additions and 44 deletions

View File

@ -538,7 +538,7 @@ static switch_xml_t switch_xml_err(switch_xml_root_t root, char *s, const char *
static char *switch_xml_decode(char *s, char **ent, char t) static char *switch_xml_decode(char *s, char **ent, char t)
{ {
char *e, *r = s, *m = s; char *e, *r = s, *m = s;
long b, c, d, l; unsigned long b, c, d, l;
for (; *s; s++) { /* normalize line endings */ for (; *s; s++) { /* normalize line endings */
while (*s == '\r') { while (*s == '\r') {
@ -555,10 +555,17 @@ static char *switch_xml_decode(char *s, char **ent, char t)
if (!*s) if (!*s)
break; break;
else if (t != 'c' && !strncmp(s, "&#", 2)) { /* character reference */ else if (t != 'c' && !strncmp(s, "&#", 2)) { /* character reference */
if (s[2] == 'x') char *code = s + 2;
c = strtol(s + 3, &e, 16); /* base 16 */ int base = 10;
else if (*code == 'x') {
c = strtol(s + 2, &e, 10); /* base 10 */ code++;
base = 16;
}
if (!isxdigit((int)*code)) { /* "&# 1;" and "&#-1;" are invalid */
s++;
continue;
}
c = strtoul(code, &e, base);
if (!c || *e != ';') { if (!c || *e != ';') {
s++; s++;
continue; continue;
@ -566,10 +573,14 @@ static char *switch_xml_decode(char *s, char **ent, char t)
/* not a character ref */ /* not a character ref */
if (c < 0x80) if (c < 0x80)
*(s++) = (char) c; /* US-ASCII subset */ *(s++) = (char) c; /* US-ASCII subset */
else { /* multi-byte UTF-8 sequence */ else if (c > 0x7FFFFFFF) { /* out of UTF-8 range */
s++;
continue;
} else { /* multi-byte UTF-8 sequence */
for (b = 0, d = c; d; d /= 2) for (b = 0, d = c; d; d /= 2)
b++; /* number of bits in c */ b++; /* number of bits in c */
b = (b - 2) / 5; /* number of bytes in payload */ b = (b - 2) / 5; /* number of bytes in payload */
assert(b < 7); /* because c <= 0x7FFFFFFF */
*(s++) = (char) ((0xFF << (7 - b)) | (c >> (6 * b))); /* head */ *(s++) = (char) ((0xFF << (7 - b)) | (c >> (6 * b))); /* head */
while (b) while (b)
*(s++) = (char) (0x80 | ((c >> (6 * --b)) & 0x3F)); /* payload */ *(s++) = (char) (0x80 | ((c >> (6 * --b)) & 0x3F)); /* payload */
@ -580,8 +591,8 @@ static char *switch_xml_decode(char *s, char **ent, char t)
for (b = 0; ent[b] && strncmp(s + 1, ent[b], strlen(ent[b])); b += 2); /* find entity in entity list */ for (b = 0; ent[b] && strncmp(s + 1, ent[b], strlen(ent[b])); b += 2); /* find entity in entity list */
if (ent[b++]) { /* found a match */ if (ent[b++]) { /* found a match */
if ((c = (long) strlen(ent[b])) - 1 > (e = strchr(s, ';')) - s) { if ((c = (unsigned long) strlen(ent[b])) - 1 > (e = strchr(s, ';')) - s) {
l = (d = (long) (s - r)) + c + (long) strlen(e); /* new length */ l = (d = (unsigned long) (s - r)) + c + (unsigned long) strlen(e); /* new length */
if (l) { if (l) {
if (r == m) { if (r == m) {
char *tmp = (char *) malloc(l); char *tmp = (char *) malloc(l);
@ -618,7 +629,7 @@ static char *switch_xml_decode(char *s, char **ent, char t)
if (t == '*') { /* normalize spaces for non-cdata attributes */ if (t == '*') { /* normalize spaces for non-cdata attributes */
for (s = r; *s; s++) { for (s = r; *s; s++) {
if ((l = (long) strspn(s, " "))) if ((l = (unsigned long) strspn(s, " ")))
memmove(s, s + l, strlen(s + l) + 1); memmove(s, s + l, strlen(s + l) + 1);
while (*s && *s != ' ') while (*s && *s != ' ')
s++; s++;
@ -2944,58 +2955,91 @@ SWITCH_DECLARE(switch_xml_t) switch_xml_set_txt(switch_xml_t xml, const char *tx
of NULL will remove the specified attribute. Returns the tag given */ of NULL will remove the specified attribute. Returns the tag given */
SWITCH_DECLARE(switch_xml_t) switch_xml_set_attr(switch_xml_t xml, const char *name, const char *value) SWITCH_DECLARE(switch_xml_t) switch_xml_set_attr(switch_xml_t xml, const char *name, const char *value)
{ {
int l = 0, c; int l = 0, attr_number, c;
/* After adding the first attribute, xml->attr = { name1, val1, name2, val2,
* ..., nameN, valN, NULL, alloc_flags }. alloc_flags tracks how the memory
* for each attr and value is managed. */
char *alloc_flags;
if (!xml) if (!xml)
return NULL; return NULL;
while (xml->attr[l] && strcmp(xml->attr[l], name)) while (xml->attr[l] && strcmp(xml->attr[l], name))
l += 2; l += 2;
attr_number = l / 2;
if (!xml->attr[l]) { /* not found, add as new attribute */ if (!xml->attr[l]) { /* not found, add as new attribute */
if (!value) if (!value)
return xml; /* nothing to do */ goto cleanup; /* not found, no need to remove attribute */
if (xml->attr == SWITCH_XML_NIL) { /* first attribute */ if (xml->attr == SWITCH_XML_NIL) { /* first attribute */
xml->attr = (char **) malloc(4 * sizeof(char *)); char ** tmp = (char **) malloc(4 * sizeof(char *));
if (!xml->attr) if (!tmp)
return NULL; goto cleanup;
xml->attr[1] = strdup(""); /* empty list of malloced names/vals */ xml->attr = tmp;
assert(l == 0); /* name of SWITCH_XML_NIL is assumed to be empty */
xml->attr[0] = NULL; /* terminator */
xml->attr[1] = NULL; /* empty list of malloced names/vals */
} else { } else {
char **tmp = (char **) realloc(xml->attr, (l + 4) * sizeof(char *)); char **tmp = (char **) realloc(xml->attr, (l + 4) * sizeof(char *));
if (!tmp) if (!tmp)
return xml; goto cleanup;
xml->attr = tmp; xml->attr = tmp;
} }
/* Extend list of allocation flags for the new attribute */
alloc_flags = (char *)realloc(xml->attr[l + 1], attr_number + 1);
if (!alloc_flags)
goto cleanup;
alloc_flags[attr_number + 1] = '\0'; /* terminate list */
/* Add new attribute, the value will be set further below. */
xml->attr[l] = (char *) name; /* set attribute name */ xml->attr[l] = (char *) name; /* set attribute name */
xml->attr[l + 2] = NULL; /* null terminate attribute list */ name = NULL; /* control of memory is transferred */
xml->attr[l + 3] = (char *) realloc(xml->attr[l + 1], (c = (int) strlen(xml->attr[l + 1])) + 2); l += 2;
strcpy(xml->attr[l + 3] + c, " "); /* set name/value as not malloced */
if (xml->flags & SWITCH_XML_DUP)
xml->attr[l + 3][c] = SWITCH_XML_NAMEM;
} else if (xml->flags & SWITCH_XML_DUP)
free((char *) name); /* name was strduped */
for (c = l; xml->attr[c]; c += 2); /* find end of attribute list */ xml->attr[l] = NULL; /* null terminate attribute list */
if (xml->attr[c + 1][l / 2] & SWITCH_XML_TXTM) xml->attr[l + 1] = alloc_flags;
free(xml->attr[l + 1]); /* old val */
if (xml->flags & SWITCH_XML_DUP) if (xml->flags & SWITCH_XML_DUP)
xml->attr[c + 1][l / 2] |= SWITCH_XML_TXTM; alloc_flags[attr_number] = SWITCH_XML_NAMEM;
else else
xml->attr[c + 1][l / 2] &= ~SWITCH_XML_TXTM; alloc_flags[attr_number] = ' '; /* dummy value */
} else {
if (value) while (xml->attr[l]) l += 2;
xml->attr[l + 1] = (char *) value; /* set attribute value */ alloc_flags = xml->attr[l + 1];
else { /* remove attribute */
char **tmp;
if (xml->attr[c + 1][l / 2] & SWITCH_XML_NAMEM)
free(xml->attr[l]);
memmove(xml->attr + l, xml->attr + l + 2, (c - l + 2) * sizeof(char *));
tmp = (char **) realloc(xml->attr, (c + 2) * sizeof(char *));
if (!tmp)
return xml;
xml->attr = tmp;
memmove(xml->attr[c + 1] + (l / 2), xml->attr[c + 1] + (l / 2) + 1, (c / 2) - (l / 2)); /* fix list of which name/vals are malloced */
} }
c = 2 * attr_number; /* index of the current attribute name */
/* l points to the index of the terminator name */
if (alloc_flags[attr_number] & SWITCH_XML_TXTM)
free(xml->attr[c + 1]); /* old val */
if (xml->flags & SWITCH_XML_DUP)
alloc_flags[attr_number] |= SWITCH_XML_TXTM;
else
alloc_flags[attr_number] &= ~SWITCH_XML_TXTM;
if (value) {
xml->attr[c + 1] = (char *) value; /* set attribute value */
value = NULL; /* control of memory is transferred */
} else { /* remove attribute */
char **tmp;
/* free name if it was dynamically allocated (value is handled above) */
if (alloc_flags[attr_number] & SWITCH_XML_NAMEM)
free(xml->attr[c]);
/* drop (name, value) pair from attribute list */
memmove(xml->attr + c, xml->attr + c + 2, (l - c) * sizeof(char *));
tmp = (char **) realloc(xml->attr, l * sizeof(char *));
if (tmp) /* try to shrink when possible */
xml->attr = tmp;
memmove(alloc_flags + attr_number, alloc_flags + attr_number + 1, (l - c) / 2); /* shrink allocation info */
}
cleanup:
if (xml->flags & SWITCH_XML_DUP) {
free((char *) name); /* name was strduped */
free((char *) value); /* name was strduped, but an error occurred */
xml->flags &= ~SWITCH_XML_DUP; /* clear strdup() flag */ xml->flags &= ~SWITCH_XML_DUP; /* clear strdup() flag */
}
return xml; return xml;
} }