diff --git a/projects/apache-httpd/Dockerfile b/projects/apache-httpd/Dockerfile new file mode 100644 index 000000000..bb6a63ee9 --- /dev/null +++ b/projects/apache-httpd/Dockerfile @@ -0,0 +1,29 @@ +# Copyright 2021 Google LLC +# +# 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. +# +################################################################################ + +FROM gcr.io/oss-fuzz-base/base-builder +RUN apt-get update && apt-get install -y make autoconf automake libtool wget libpcre3 \ + libpcre3-dev uuid-dev pkg-config libtool-bin +RUN wget https://github.com/libexpat/libexpat/releases/download/R_2_4_1/expat-2.4.1.tar.gz && \ + tar -xf expat-2.4.1.tar.gz && \ + cd expat-2.4.1 && \ + ./configure && \ + make && \ + make install +RUN svn checkout https://svn.apache.org/repos/asf/httpd/httpd/trunk/ httpd +WORKDIR httpd +COPY build.sh $SRC/ +COPY fuzz_*.c $SRC/ diff --git a/projects/apache-httpd/build.sh b/projects/apache-httpd/build.sh new file mode 100755 index 000000000..a1383577b --- /dev/null +++ b/projects/apache-httpd/build.sh @@ -0,0 +1,41 @@ +#!/bin/bash -eu +# Copyright 2021 Google LLC +# +# 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. +# +################################################################################ + +unset CPP +unset CXX + +# Download apr and place in httpd srclib folder. Apr-2.0 includes apr-utils +svn checkout https://svn.apache.org/repos/asf/apr/apr/trunk/ srclib/apr + +# Build httpd +./buildconf +./configure --with-included-apr --enable-pool-debug +make + +# Build the fuzzers +for fuzzname in utils parse tokenize addr_parse; do + $CC $CFLAGS $LIB_FUZZING_ENGINE -I./include -I./os/unix -I./srclib/apr/include -I./srclib/apr-util/include/ \ + $SRC/fuzz_${fuzzname}.c -o $OUT/fuzz_${fuzzname} \ + ./modules.o buildmark.o \ + -Wl,--start-group ./server/.libs/libmain.a \ + ./modules/core/.libs/libmod_so.a \ + ./modules/http/.libs/libmod_http.a \ + ./server/mpm/event/.libs/libevent.a \ + ./os/unix/.libs/libos.a \ + ./srclib/apr/.libs/libapr-2.a \ + -Wl,--end-group -luuid -lpcre -lcrypt +done diff --git a/projects/apache-httpd/fuzz_addr_parse.c b/projects/apache-httpd/fuzz_addr_parse.c new file mode 100644 index 000000000..3fc00ab6b --- /dev/null +++ b/projects/apache-httpd/fuzz_addr_parse.c @@ -0,0 +1,37 @@ +/* Copyright 2021 Google LLC +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 +#include +#include + +#include "apr_network_io.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + apr_pool_t *pool; + apr_pool_initialize(); + if (apr_pool_create(&pool, NULL) != APR_SUCCESS) { + abort(); + } + + char *addr = NULL; + char *scope_id = NULL; + apr_port_t port = 0; + char *input_string = strndup((const char *)data, size); + + apr_parse_addr_port(&addr, &scope_id, &port, input_string, pool); + + free(input_string); + apr_pool_destroy(pool); + apr_terminate(); + + return 0; +} diff --git a/projects/apache-httpd/fuzz_parse.c b/projects/apache-httpd/fuzz_parse.c new file mode 100644 index 000000000..fb87db532 --- /dev/null +++ b/projects/apache-httpd/fuzz_parse.c @@ -0,0 +1,70 @@ +/* Copyright 2021 Google LLC +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_file_io.h" +#include "apr_poll.h" +#include "apr_portable.h" +#include "apr_proc_mutex.h" +#include "apr_signal.h" +#include "apr_strings.h" +#include "apr_thread_mutex.h" +#include "apr_thread_proc.h" + +#define APR_WANT_STRFUNC +#include "apr_file_io.h" +#include "apr_fnmatch.h" +#include "apr_want.h" + +#include "apr_poll.h" +#include "apr_want.h" + +#include "ap_config.h" +#include "ap_expr.h" +#include "ap_listen.h" +#include "ap_provider.h" +#include "ap_regex.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *new_str = (char *)malloc(size + 1); + if (new_str == NULL) { + return 0; + } + memcpy(new_str, data, size); + new_str[size] = '\0'; + + apr_pool_initialize(); + apr_pool_t *v = NULL; + apr_pool_create(&v, NULL); + + int only_ascii = 1; + for (int i = 0; i < size; i++) { + // Avoid unnessary exits because of non-ascii characters. + if (new_str[i] < 0x01 || new_str[i] > 0x7f) { + only_ascii = 0; + } + // Avoid forced exits beause of, e.g. unsupported characters or recursion + // depth + if (new_str[i] == 0x5c || new_str[i] == '{') { + only_ascii = 0; + } + } + + // Now parse + if (only_ascii) { + ap_expr_info_t val; + ap_expr_parse(v, v, &val, new_str, NULL); + } + + apr_pool_terminate(); + free(new_str); + return 0; +} diff --git a/projects/apache-httpd/fuzz_tokenize.c b/projects/apache-httpd/fuzz_tokenize.c new file mode 100644 index 000000000..1de629558 --- /dev/null +++ b/projects/apache-httpd/fuzz_tokenize.c @@ -0,0 +1,34 @@ +/* Copyright 2021 Google LLC +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 +#include +#include + +#include "apr_strings.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + apr_pool_t *pool; + apr_pool_initialize(); + if (apr_pool_create(&pool, NULL) != APR_SUCCESS) { + abort(); + } + + char *arg_str = strndup((const char *)data, size); + char **argv_out; + apr_tokenize_to_argv(arg_str, &argv_out, pool); + + free(arg_str); + apr_pool_destroy(pool); + apr_terminate(); + + return 0; +} diff --git a/projects/apache-httpd/fuzz_utils.c b/projects/apache-httpd/fuzz_utils.c new file mode 100644 index 000000000..1ced86fe5 --- /dev/null +++ b/projects/apache-httpd/fuzz_utils.c @@ -0,0 +1,131 @@ +/* Copyright 2021 Google LLC +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_file_io.h" +#include "apr_poll.h" +#include "apr_portable.h" +#include "apr_proc_mutex.h" +#include "apr_signal.h" +#include "apr_strings.h" +#include "apr_thread_mutex.h" +#include "apr_thread_proc.h" + +#define APR_WANT_STRFUNC +#include "apr_file_io.h" +#include "apr_fnmatch.h" +#include "apr_want.h" + +#include "apr_poll.h" +#include "apr_want.h" + +#include "ap_config.h" +#include "ap_expr.h" +#include "ap_listen.h" +#include "ap_provider.h" +#include "ap_regex.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *new_str = (char *)malloc(size + 1); + if (new_str == NULL) { + return 0; + } + memcpy(new_str, data, size); + new_str[size] = '\0'; + + // Targets that do not require a pool + ap_cstr_casecmp(new_str, new_str); + ap_getparents(new_str); + ap_unescape_url(new_str); + ap_unescape_urlencoded(new_str); + ap_strcmp_match(new_str, "AAAAAABDKJSAD"); + + // Pool initialisation + if (apr_pool_initialize() == APR_SUCCESS) { + apr_pool_t *pool = NULL; + apr_pool_create(&pool, NULL); + + // Targets that require a pool + ap_field_noparam(pool, new_str); + + ap_escape_shell_cmd(pool, new_str); + ap_os_escape_path(pool, new_str, 0); + ap_escape_html2(pool, new_str, 0); + ap_escape_logitem(pool, new_str); + + // This line causes some issues if something bad is allocated + ap_escape_quotes(pool, new_str); + + if (size > 2) { + ap_cstr_casecmpn(new_str, new_str + 2, size - 2); + } + + char *d = malloc(size * 2); + ap_escape_errorlog_item(d, new_str, size * 2); + free(d); + + // base64 + char *decoded = NULL; + decoded = ap_pbase64decode(pool, new_str); + ap_pbase64encode(pool, new_str); + + char *tmp_s = new_str; + ap_getword_conf2(pool, &tmp_s); + + // str functions + ap_strcasecmp_match(tmp_s, "asdfkj"); + ap_strcasestr(tmp_s, "AAAAAAAAAAAAAA"); + ap_strcasestr(tmp_s, "AasdfasbA"); + ap_strcasestr(tmp_s, "1341234"); + ap_strcasestr("AAAAAAAAAAAAAA", tmp_s); + ap_strcasestr("AasdfasbA", tmp_s); + ap_strcasestr("1341234", tmp_s); + + // List functions + tmp_s = new_str; + ap_get_list_item(pool, &tmp_s); + tmp_s = new_str; + ap_find_list_item(pool, &tmp_s, "kjahsdfkj"); + ap_find_token(pool, tmp_s, "klsjdfk"); + ap_find_last_token(pool, tmp_s, "sdadf"); + ap_is_chunked(pool, tmp_s); + + apr_array_header_t *offers = NULL; + ap_parse_token_list_strict(pool, new_str, &offers, 0); + + char *tmp_null = NULL; + ap_pstr2_alnum(pool, new_str, &tmp_null); + + // Word functions + tmp_s = new_str; + ap_getword(pool, &tmp_s, 0); + + tmp_s = new_str; + ap_getword_white_nc(pool, &tmp_s); + + tmp_s = new_str; + ap_get_token(pool, &tmp_s, 1); + + tmp_s = new_str; + ap_escape_urlencoded(pool, tmp_s); + + apr_interval_time_t timeout; + ap_timeout_parameter_parse(new_str, &timeout, "ms"); + + tmp_s = new_str; + ap_content_type_tolower(tmp_s); + + // Cleanup + apr_pool_terminate(); + } + free(new_str); + return 0; +} diff --git a/projects/apache-httpd/project.yaml b/projects/apache-httpd/project.yaml new file mode 100644 index 000000000..e1c73e9c8 --- /dev/null +++ b/projects/apache-httpd/project.yaml @@ -0,0 +1,8 @@ +homepage: "https://httpd.apache.org/" +language: c +primary_contact: "david@adalogics.com" +auto_ccs: + - "stefan.eissing@greenbytes.de" + - "covener@gmail.com" + - "ylavic.dev@gmail.com" +main_repo: "https://github.com/apache/httpd"