1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr.h"
18#include "apr_general.h"
19#include "apr_xml.h"
20#include "abts.h"
21#include "testutil.h"
22
23static apr_status_t create_dummy_file_error(abts_case *tc, apr_pool_t *p,
24                                            apr_file_t **fd)
25{
26    int i;
27    apr_status_t rv;
28    apr_off_t off = 0L;
29    char template[] = "data/testxmldummyerrorXXXXXX";
30
31    rv = apr_file_mktemp(fd, template, APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE | APR_FOPEN_DELONCLOSE |
32                         APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_EXCL, p);
33    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
34
35    if (rv != APR_SUCCESS)
36        return rv;
37
38    rv = apr_file_puts("<?xml version=\"1.0\" ?>\n<maryx>"
39                       "<had a=\"little\"/><lamb/>\n", *fd);
40    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
41
42    for (i = 0; i < 5000; i++) {
43        rv = apr_file_puts("<hmm roast=\"lamb\" "
44                           "for=\"dinner\">yummy</hmm>\n", *fd);
45        ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
46    }
47
48    rv = apr_file_puts("</mary>\n", *fd);
49    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
50
51    rv = apr_file_seek(*fd, APR_SET, &off);
52    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
53
54    return rv;
55}
56
57static apr_status_t create_dummy_file(abts_case *tc, apr_pool_t *p,
58                                      apr_file_t **fd)
59{
60    int i;
61    apr_status_t rv;
62    apr_off_t off = 0L;
63    char template[] = "data/testxmldummyXXXXXX";
64
65    rv = apr_file_mktemp(fd, template, APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE | APR_FOPEN_DELONCLOSE |
66                         APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_EXCL, p);
67    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
68
69    if (rv != APR_SUCCESS)
70        return rv;
71
72    rv = apr_file_puts("<?xml version=\"1.0\" ?>\n<mary>\n", *fd);
73    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
74
75    for (i = 0; i < 5000; i++) {
76        rv = apr_file_puts("<hmm roast=\"lamb\" "
77                           "for=\"dinner &lt;&gt;&#x3D;\">yummy</hmm>\n", *fd);
78        ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
79    }
80
81    rv = apr_file_puts("</mary>\n", *fd);
82    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
83
84    rv = apr_file_seek(*fd, APR_SET, &off);
85    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
86
87    return rv;
88}
89
90static void dump_xml(abts_case *tc, apr_xml_elem *e, int level)
91{
92    apr_xml_attr *a;
93    apr_xml_elem *ec;
94
95    if (level == 0) {
96        ABTS_STR_EQUAL(tc, "mary", e->name);
97    } else {
98        ABTS_STR_EQUAL(tc, "hmm", e->name);
99    }
100
101    if (e->attr) {
102        a = e->attr;
103        ABTS_PTR_NOTNULL(tc, a);
104        ABTS_STR_EQUAL(tc, "for", a->name);
105        ABTS_STR_EQUAL(tc, "dinner <>=", a->value);
106        a = a->next;
107        ABTS_PTR_NOTNULL(tc, a);
108        ABTS_STR_EQUAL(tc, "roast", a->name);
109        ABTS_STR_EQUAL(tc, "lamb", a->value);
110    }
111    if (e->first_child) {
112        ec = e->first_child;
113        while (ec) {
114            dump_xml(tc, ec, level + 1);
115            ec = ec->next;
116        }
117    }
118}
119
120static void test_xml_parser(abts_case *tc, void *data)
121{
122    apr_file_t *fd;
123    apr_xml_parser *parser;
124    apr_xml_doc *doc;
125    apr_status_t rv;
126
127    rv = create_dummy_file(tc, p, &fd);
128    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
129
130    if (rv != APR_SUCCESS)
131        return;
132
133    rv = apr_xml_parse_file(p, &parser, &doc, fd, 2000);
134    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
135
136    dump_xml(tc, doc->root, 0);
137
138    rv = apr_file_close(fd);
139    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
140
141    rv = create_dummy_file_error(tc, p, &fd);
142    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
143
144    if (rv != APR_SUCCESS)
145        return;
146
147    rv = apr_xml_parse_file(p, &parser, &doc, fd, 2000);
148    ABTS_TRUE(tc, rv != APR_SUCCESS);
149}
150
151static void test_billion_laughs(abts_case *tc, void *data)
152{
153    apr_file_t *fd;
154    apr_xml_parser *parser;
155    apr_xml_doc *doc;
156    apr_status_t rv;
157
158    rv = apr_file_open(&fd, "data/billion-laughs.xml",
159                       APR_FOPEN_READ, 0, p);
160    apr_assert_success(tc, "open billion-laughs.xml", rv);
161
162    /* Don't test for return value; if it returns, chances are the bug
163     * is fixed or the machine has insane amounts of RAM. */
164    apr_xml_parse_file(p, &parser, &doc, fd, 2000);
165
166    apr_file_close(fd);
167}
168
169static void test_CVE_2009_3720_alpha(abts_case *tc, void *data)
170{
171    apr_xml_parser *xp;
172    apr_xml_doc *doc;
173    apr_status_t rv;
174
175    xp = apr_xml_parser_create(p);
176
177    rv = apr_xml_parser_feed(xp, "\0\r\n", 3);
178    if (rv == APR_SUCCESS)
179        apr_xml_parser_done(xp, &doc);
180}
181
182static void test_CVE_2009_3720_beta(abts_case *tc, void *data)
183{
184    apr_xml_parser *xp;
185    apr_xml_doc *doc;
186    apr_status_t rv;
187
188    xp = apr_xml_parser_create(p);
189
190    rv = apr_xml_parser_feed(xp, "<?xml version\xc2\x85='1.0'?>\r\n", 25);
191    if (rv == APR_SUCCESS)
192        apr_xml_parser_done(xp, &doc);
193}
194
195abts_suite *testxml(abts_suite *suite)
196{
197    suite = ADD_SUITE(suite);
198
199    abts_run_test(suite, test_xml_parser, NULL);
200    abts_run_test(suite, test_billion_laughs, NULL);
201    abts_run_test(suite, test_CVE_2009_3720_alpha, NULL);
202    abts_run_test(suite, test_CVE_2009_3720_beta, NULL);
203
204    return suite;
205}
206