diff -u libxml2-2.7.8.dfsg/parser.c libxml2-2.7.8.dfsg/parser.c --- libxml2-2.7.8.dfsg/parser.c +++ libxml2-2.7.8.dfsg/parser.c @@ -124,6 +124,29 @@ return (0); if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP) return (1); + + /* + * This may look absurd but is needed to detect + * entities problems + */ + if ((ent != NULL) && (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) && + (ent->content != NULL) && (ent->checked == 0)) { + unsigned long oldnbent = ctxt->nbentities; + xmlChar *rep; + + ent->checked = 1; + + rep = xmlStringDecodeEntities(ctxt, ent->content, + XML_SUBSTITUTE_REF, 0, 0, 0); + + ent->checked = (ctxt->nbentities - oldnbent + 1) * 2; + if (rep != NULL) { + if (xmlStrchr(rep, '<')) + ent->checked |= 1; + xmlFree(rep); + rep = NULL; + } + } if (replacement != 0) { if (replacement < XML_MAX_TEXT_LENGTH) return(0); @@ -164,7 +187,7 @@ /* * use the number of parsed entities in the replacement */ - size = ent->checked; + size = ent->checked / 2; /* * The amount of data parsed counting entities size only once @@ -183,9 +206,12 @@ return (0); } else { /* - * strange we got no data for checking just return + * strange we got no data for checking */ - return (0); + if (((ctxt->lastError.code != XML_ERR_UNDECLARED_ENTITY) && + (ctxt->lastError.code != XML_WAR_UNDECLARED_ENTITY)) || + (ctxt->nbentities <= 10000)) + return (0); } xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); return (1); @@ -2546,6 +2572,7 @@ name, NULL); ctxt->valid = 0; } + xmlParserEntityCheck(ctxt, 0, NULL, 0); } else if (ctxt->input->free != deallocblankswrapper) { input = xmlNewBlanksWrapperInputStream(ctxt, entity); if (xmlPushInput(ctxt, input) < 0) @@ -2716,8 +2743,9 @@ if ((ctxt->lastError.code == XML_ERR_ENTITY_LOOP) || (ctxt->lastError.code == XML_ERR_INTERNAL_ERROR)) goto int_error; + xmlParserEntityCheck(ctxt, 0, ent, 0); if (ent != NULL) - ctxt->nbentities += ent->checked; + ctxt->nbentities += ent->checked / 2; if ((ent != NULL) && (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) { if (ent->content != NULL) { @@ -2767,8 +2795,9 @@ ent = xmlParseStringPEReference(ctxt, &str); if (ctxt->lastError.code == XML_ERR_ENTITY_LOOP) goto int_error; + xmlParserEntityCheck(ctxt, 0, ent, 0); if (ent != NULL) - ctxt->nbentities += ent->checked; + ctxt->nbentities += ent->checked / 2; if (ent != NULL) { if (ent->content == NULL) { xmlLoadEntityContent(ctxt, ent); @@ -3946,10 +3975,16 @@ * entities problems */ if ((ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) && - (ent->content != NULL)) { + (ent->content != NULL) && (ent->checked == 0)) { + unsigned long oldnbent = ctxt->nbentities; + rep = xmlStringDecodeEntities(ctxt, ent->content, XML_SUBSTITUTE_REF, 0, 0, 0); + + ent->checked = (ctxt->nbentities - oldnbent + 1) * 2; if (rep != NULL) { + if (xmlStrchr(rep, '<')) + ent->checked |= 1; xmlFree(rep); rep = NULL; } @@ -7069,7 +7104,9 @@ * Store the number of entities needing parsing for this entity * content and do checkings */ - ent->checked = ctxt->nbentities - oldnbent; + ent->checked = (ctxt->nbentities - oldnbent + 1) * 2; + if ((ent->content != NULL) && (xmlStrchr(ent->content, '<'))) + ent->checked |= 1; if (ret == XML_ERR_ENTITY_LOOP) { xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); xmlFreeNodeList(list); @@ -7128,14 +7165,15 @@ (ret != XML_WAR_UNDECLARED_ENTITY)) { xmlFatalErrMsgStr(ctxt, XML_ERR_UNDECLARED_ENTITY, "Entity '%s' failed to parse\n", ent->name); + xmlParserEntityCheck(ctxt, 0, ent, 0); } else if (list != NULL) { xmlFreeNodeList(list); list = NULL; } if (ent->checked == 0) - ent->checked = 1; + ent->checked = 2; } else if (ent->checked != 1) { - ctxt->nbentities += ent->checked; + ctxt->nbentities += ent->checked / 2; } /* @@ -7236,7 +7274,7 @@ /* * We are copying here, make sure there is no abuse */ - ctxt->sizeentcopy += ent->length; + ctxt->sizeentcopy += ent->length + 5; if (xmlParserEntityCheck(ctxt, 0, ent, ctxt->sizeentcopy)) return; @@ -7284,7 +7322,7 @@ /* * We are copying here, make sure there is no abuse */ - ctxt->sizeentcopy += ent->length; + ctxt->sizeentcopy += ent->length + 5; if (xmlParserEntityCheck(ctxt, 0, ent, ctxt->sizeentcopy)) return; @@ -7470,6 +7508,7 @@ ctxt->sax->reference(ctxt->userData, name); } } + xmlParserEntityCheck(ctxt, 0, ent, 0); ctxt->valid = 0; } @@ -7500,11 +7539,13 @@ * not contain a <. */ else if ((ctxt->instate == XML_PARSER_ATTRIBUTE_VALUE) && - (ent != NULL) && (ent->content != NULL) && - (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) && - (xmlStrchr(ent->content, '<'))) { - xmlFatalErrMsgStr(ctxt, XML_ERR_LT_IN_ATTRIBUTE, - "'<' in entity '%s' is not allowed in attributes values\n", name); + (ent != NULL) && + (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY)) { + if ((ent->checked & 1) || ((ent->checked == 0) && + (ent->content != NULL) &&(xmlStrchr(ent->content, '<')))) { + xmlFatalErrMsgStr(ctxt, XML_ERR_LT_IN_ATTRIBUTE, + "'<' in entity '%s' is not allowed in attributes values\n", name); + } } /* @@ -7661,6 +7702,7 @@ "Entity '%s' not defined\n", name); } + xmlParserEntityCheck(ctxt, 0, ent, 0); /* TODO ? check regressions ctxt->valid = 0; */ } @@ -7820,6 +7862,7 @@ name, NULL); ctxt->valid = 0; } + xmlParserEntityCheck(ctxt, 0, NULL, 0); } else { /* * Internal checking in case the entity quest barfed @@ -8050,6 +8093,7 @@ name, NULL); ctxt->valid = 0; } + xmlParserEntityCheck(ctxt, 0, NULL, 0); } else { /* * Internal checking in case the entity quest barfed diff -u libxml2-2.7.8.dfsg/debian/changelog libxml2-2.7.8.dfsg/debian/changelog --- libxml2-2.7.8.dfsg/debian/changelog +++ libxml2-2.7.8.dfsg/debian/changelog @@ -1,3 +1,15 @@ +libxml2 (2.7.8.dfsg-5.1ubuntu4.11) precise-security; urgency=medium + + * SECURITY UPDATE: denial of service via entity expansion + - parser.c, SAX2.c, include/libxml/entities.h: refactor entity checking + and add additional tests. + - https://git.gnome.org/browse/libxml2/commit/?id=a3f1e3e5712257fd279917a9158278534e8f4b72 + - https://git.gnome.org/browse/libxml2/commit/?id=cff2546f13503ac028e4c1f63c7b6d85f2f2d777 + - https://git.gnome.org/browse/libxml2/commit/?id=be2a7edaf289c5da74a4f9ed3a0b6c733e775230 + - CVE-2014-3660 + + -- Marc Deslauriers Wed, 22 Oct 2014 12:16:42 -0400 + libxml2 (2.7.8.dfsg-5.1ubuntu4.9) precise-security; urgency=medium * SECURITY REGRESSION: more xmllint regressions (LP: #1321869) only in patch2: unchanged: --- libxml2-2.7.8.dfsg.orig/SAX2.c +++ libxml2-2.7.8.dfsg/SAX2.c @@ -574,6 +574,7 @@ * parse the external entity */ xmlNodePtr children; + unsigned long oldnbent = ctxt->nbentities; val = xmlParseCtxtExternalEntity(ctxt, ret->URI, ret->ExternalID, &children); @@ -586,8 +587,11 @@ return(NULL); } ret->owner = 1; - if (ret->checked == 0) - ret->checked = 1; + if (ret->checked == 0) { + ret->checked = (ctxt->nbentities - oldnbent + 1) * 2; + if ((ret->content != NULL) && (xmlStrchr(ret->content, '<'))) + ret->checked |= 1; + } } return(ret); } only in patch2: unchanged: --- libxml2-2.7.8.dfsg.orig/include/libxml/entities.h +++ libxml2-2.7.8.dfsg/include/libxml/entities.h @@ -58,7 +58,8 @@ int owner; /* does the entity own the childrens */ int checked; /* was the entity content checked */ /* this is also used to count entites - * references done from that entity */ + * references done from that entity + * and if it contains '<' */ }; /*