/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "apr.h" #include "apr_general.h" #include "apr_xml.h" #if APR_HAVE_STDLIB_H #include /* for exit() */ #endif static const char *progname; static const char *usage = "%s [xmlfile]\nIt will create " "a dummy XML file if none is supplied"; /* * If our platform knows about the tmpnam() external buffer size, create * a buffer to pass in. This is needed in a threaded environment, or * one that thinks it is (like HP-UX). */ #ifdef L_tmpnam static char tname_buf[L_tmpnam]; #else static char *tname_buf = NULL; #endif static apr_status_t create_dummy_file_error(apr_pool_t *p, apr_file_t **fd) { apr_status_t rv; char *tmpfile; int i; apr_off_t off = 0L; tmpfile = tmpnam(tname_buf); if ((tmpfile == NULL) || (*tmpfile == '\0')) { fprintf(stderr, "unable to generate temporary filename\n"); if (errno == 0) { errno = ENOENT; } perror("tmpnam"); return APR_ENOENT; } rv = apr_file_open(fd, tmpfile, APR_CREATE|APR_TRUNCATE|APR_DELONCLOSE| APR_READ|APR_WRITE|APR_EXCL, APR_OS_DEFAULT, p); if (rv != APR_SUCCESS) return rv; rv = apr_file_puts("\n" "\n", *fd); if (rv != APR_SUCCESS) return rv; for (i = 0; i < 5000; i++) { rv = apr_file_puts("yummy\n", *fd); if (rv != APR_SUCCESS) return rv; } rv = apr_file_puts("\n", *fd); if (rv != APR_SUCCESS) return rv; return apr_file_seek(*fd, APR_SET, &off); } static apr_status_t create_dummy_file(apr_pool_t *p, apr_file_t **fd) { apr_status_t rv; char *tmpfile; int i; apr_off_t off = 0L; tmpfile = tmpnam(tname_buf); if ((tmpfile == NULL) || (*tmpfile == '\0')) { fprintf(stderr, "unable to generate temporary filename\n"); if (errno == 0) { errno = ENOENT; } perror("tmpnam"); return APR_ENOENT; } rv = apr_file_open(fd, tmpfile, APR_CREATE|APR_TRUNCATE|APR_DELONCLOSE| APR_READ|APR_WRITE|APR_EXCL, APR_OS_DEFAULT, p); if (rv != APR_SUCCESS) return rv; rv = apr_file_puts("\n" "\n", *fd); if (rv != APR_SUCCESS) return rv; for (i = 0; i < 5000; i++) { rv = apr_file_puts("yummy\n", *fd); if (rv != APR_SUCCESS) return rv; } rv = apr_file_puts("\n", *fd); if (rv != APR_SUCCESS) return rv; rv = apr_file_seek(*fd, APR_SET, &off); return rv; } static void dump_xml(apr_xml_elem *e, int level) { apr_xml_attr *a; apr_xml_elem *ec; printf("%d: element %s\n", level, e->name); if (e->attr) { a = e->attr; printf("%d:\tattrs\t", level); while (a) { printf("%s=%s\t", a->name, a->value); a = a->next; } printf("\n"); } if (e->first_child) { ec = e->first_child; while (ec) { dump_xml(ec, level + 1); ec = ec->next; } } } static void oops(const char *s1, const char *s2, apr_status_t rv) { if (progname) fprintf(stderr, "%s: ", progname); fprintf(stderr, s1, s2); if (rv != APR_SUCCESS) { char buf[120]; fprintf(stderr, " (%s)", apr_strerror(rv, buf, sizeof buf)); } fprintf(stderr, "\n"); exit(1); } static int test_xml_parser(apr_pool_t *pool, const char *file) { apr_file_t *fd; apr_xml_parser *parser; apr_xml_doc *doc; apr_status_t rv; char errbuf[2000]; char errbufXML[2000]; if (file == NULL) { rv = create_dummy_file(pool, &fd); if (rv != APR_SUCCESS) { oops("cannot create dummy file", "oops", rv); } } else { rv = apr_file_open(&fd, file, APR_READ, APR_OS_DEFAULT, pool); if (rv != APR_SUCCESS) { oops("cannot open: %s", file, rv); } } rv = apr_xml_parse_file(pool, &parser, &doc, fd, 2000); if (rv != APR_SUCCESS) { fprintf(stderr, "APR Error %s\nXML Error: %s\n", apr_strerror(rv, errbuf, sizeof(errbuf)), apr_xml_parser_geterror(parser, errbufXML, sizeof(errbufXML))); return rv; } dump_xml(doc->root, 0); rv = apr_file_close(fd); if (file == NULL) { rv = create_dummy_file_error(pool, &fd); if (rv != APR_SUCCESS) { oops("cannot create error dummy file", "oops", rv); } rv = apr_xml_parse_file(pool, &parser, &doc, fd, 2000); if (rv != APR_SUCCESS) { fprintf(stdout, "APR Error %s\nXML Error: %s " "(EXPECTED) This is good.\n", apr_strerror(rv, errbuf, sizeof(errbuf)), apr_xml_parser_geterror(parser, errbufXML, sizeof(errbufXML))); rv = APR_SUCCESS; /* reset the return code, as the test is supposed to get this error */ } else { fprintf(stderr, "Expected an error, but didn't get one ;( "); return APR_EGENERAL; } } return rv; } static void test_billion_laughs(apr_pool_t *pool) { apr_file_t *fd; apr_xml_parser *parser; apr_xml_doc *doc; apr_status_t rv; char errbuf[2000]; rv = apr_file_open(&fd, "data/billion-laughs.xml", APR_READ, 0, pool); if (rv != APR_SUCCESS) { fprintf(stderr, "APR Error %s\n", apr_strerror(rv, errbuf, sizeof(errbuf))); } /* Don't test for return value; if it returns, chances are the bug * is fixed or the machine has insane amounts of RAM. */ apr_xml_parse_file(pool, &parser, &doc, fd, 2000); apr_file_close(fd); } static void test_CVE_2009_3720_alpha(apr_pool_t *pool) { apr_xml_parser *xp; apr_xml_doc *doc; apr_status_t rv; xp = apr_xml_parser_create(pool); rv = apr_xml_parser_feed(xp, "\0\r\n", 3); if (rv == APR_SUCCESS) apr_xml_parser_done(xp, &doc); } static void test_CVE_2009_3720_beta(apr_pool_t *pool) { apr_xml_parser *xp; apr_xml_doc *doc; apr_status_t rv; xp = apr_xml_parser_create(pool); rv = apr_xml_parser_feed(xp, "\r\n", 25); if (rv == APR_SUCCESS) apr_xml_parser_done(xp, &doc); } int main(int argc, const char *const * argv) { apr_pool_t *pool; apr_status_t rv; (void) apr_initialize(); apr_pool_create(&pool, NULL); progname = argv[0]; if (argc == 1) { rv = test_xml_parser(pool, NULL); } else { if (argc == 2) { rv = test_xml_parser(pool, argv[1]); } else { oops("usage: %s", usage, 0); } } test_billion_laughs(pool); test_CVE_2009_3720_alpha(pool); test_CVE_2009_3720_beta(pool); apr_pool_destroy(pool); apr_terminate(); return rv; }