diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2012-08-14 18:25:29 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2012-08-14 18:31:16 -0400 |
commit | 17351fce4e03f0e448f9332d4774b7d5615d1e48 (patch) | |
tree | c93a4f1eb9a2511161c910810813c9fe631a0464 /src/backend/utils/adt/xml.c | |
parent | 03bda4535ee119d3dae7226faebed089925ace7e (diff) | |
download | postgresql-17351fce4e03f0e448f9332d4774b7d5615d1e48.tar.gz postgresql-17351fce4e03f0e448f9332d4774b7d5615d1e48.zip |
Prevent access to external files/URLs via XML entity references.
xml_parse() would attempt to fetch external files or URLs as needed to
resolve DTD and entity references in an XML value, thus allowing
unprivileged database users to attempt to fetch data with the privileges
of the database server. While the external data wouldn't get returned
directly to the user, portions of it could be exposed in error messages
if the data didn't parse as valid XML; and in any case the mere ability
to check existence of a file might be useful to an attacker.
The ideal solution to this would still allow fetching of references that
are listed in the host system's XML catalogs, so that documents can be
validated according to installed DTDs. However, doing that with the
available libxml2 APIs appears complex and error-prone, so we're not going
to risk it in a security patch that necessarily hasn't gotten wide review.
So this patch merely shuts off all access, causing any external fetch to
silently expand to an empty string. A future patch may improve this.
In HEAD and 9.2, also suppress warnings about undefined entities, which
would otherwise occur as a result of not loading referenced DTDs. Previous
branches don't show such warnings anyway, due to different error handling
arrangements.
Credit to Noah Misch for first reporting the problem, and for much work
towards a solution, though this simplistic approach was not his preference.
Also thanks to Daniel Veillard for consultation.
Security: CVE-2012-3489
Diffstat (limited to 'src/backend/utils/adt/xml.c')
-rw-r--r-- | src/backend/utils/adt/xml.c | 43 |
1 files changed, 41 insertions, 2 deletions
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index a9150e85692..a358de68728 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -48,6 +48,7 @@ #ifdef USE_LIBXML #include <libxml/chvalid.h> #include <libxml/parser.h> +#include <libxml/parserInternals.h> #include <libxml/tree.h> #include <libxml/uri.h> #include <libxml/xmlerror.h> @@ -99,8 +100,12 @@ struct PgXmlErrorContext /* previous libxml error handling state (saved by pg_xml_init) */ xmlStructuredErrorFunc saved_errfunc; void *saved_errcxt; + /* previous libxml entity handler (saved by pg_xml_init) */ + xmlExternalEntityLoader saved_entityfunc; }; +static xmlParserInputPtr xmlPgEntityLoader(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt); static void xml_errorHandler(void *data, xmlErrorPtr error); static void xml_ereport_by_code(int level, int sqlcode, const char *msg, int errcode); @@ -985,6 +990,13 @@ pg_xml_init(PgXmlStrictness strictness) " being used is not compatible with the libxml2" " header files that PostgreSQL was built with."))); + /* + * Also, install an entity loader to prevent unwanted fetches of external + * files and URLs. + */ + errcxt->saved_entityfunc = xmlGetExternalEntityLoader(); + xmlSetExternalEntityLoader(xmlPgEntityLoader); + return errcxt; } @@ -1027,8 +1039,9 @@ pg_xml_done(PgXmlErrorContext *errcxt, bool isError) if (cur_errcxt != (void *) errcxt) elog(WARNING, "libxml error handling state is out of sync with xml.c"); - /* Restore the saved handler */ + /* Restore the saved handlers */ xmlSetStructuredErrorFunc(errcxt->saved_errcxt, errcxt->saved_errfunc); + xmlSetExternalEntityLoader(errcxt->saved_entityfunc); /* * Mark the struct as invalid, just in case somebody somehow manages to @@ -1473,6 +1486,25 @@ xml_pstrdup(const char *string) /* + * xmlPgEntityLoader --- entity loader callback function + * + * Silently prevent any external entity URL from being loaded. We don't want + * to throw an error, so instead make the entity appear to expand to an empty + * string. + * + * We would prefer to allow loading entities that exist in the system's + * global XML catalog; but the available libxml2 APIs make that a complex + * and fragile task. For now, just shut down all external access. + */ +static xmlParserInputPtr +xmlPgEntityLoader(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) +{ + return xmlNewStringInputStream(ctxt, (const xmlChar *) ""); +} + + +/* * xml_ereport --- report an XML-related error * * The "msg" is the SQL-level message; some can be adopted from the SQL/XML @@ -1566,7 +1598,14 @@ xml_errorHandler(void *data, xmlErrorPtr error) case XML_FROM_NONE: case XML_FROM_MEMORY: case XML_FROM_IO: - /* Accept error regardless of the parsing purpose */ + /* + * Suppress warnings about undeclared entities. We need to do + * this to avoid problems due to not loading DTD definitions. + */ + if (error->code == XML_WAR_UNDECLARED_ENTITY) + return; + + /* Otherwise, accept error regardless of the parsing purpose */ break; default: |