From 9386113166fdad850de51f1ada0dc40acf4eec43 Mon Sep 17 00:00:00 2001 From: Artur Cygan Date: Mon, 12 Dec 2022 18:56:07 +0100 Subject: [PATCH] Fuzzing improvements --- fuzz/.gitignore | 2 + fuzz/README.md | 19 + fuzz/openvpn-fuzz.py | 158 +++++ fuzz/shell.nix | 19 + fuzz/src/fuzz_base64.c | 43 ++ fuzz/src/fuzz_buffer.c | 273 ++++++++ fuzz/src/fuzz_dhcp.c | 37 ++ fuzz/src/fuzz_forward.c | 227 +++++++ fuzz/src/fuzz_list.c | 137 ++++ fuzz/src/fuzz_misc.c | 79 +++ fuzz/src/fuzz_mroute.c | 70 +++ fuzz/src/fuzz_mss.c | 28 + fuzz/src/fuzz_packet_id.c | 105 ++++ fuzz/src/fuzz_parse_argv.c | 47 ++ fuzz/src/fuzz_proxy.c | 137 ++++ fuzz/src/fuzz_randomizer.cpp | 114 ++++ fuzz/src/fuzz_randomizer.h | 28 + fuzz/src/fuzz_route.c | 202 ++++++ fuzz/src/fuzz_verify_cert.c | 167 +++++ fuzz/src/fuzz_verify_cert.h | 1065 ++++++++++++++++++++++++++++++++ src/openvpn/console_builtin.c | 16 + src/openvpn/console_systemd.c | 4 + src/openvpn/error.c | 4 + src/openvpn/fake_fuzz_header.h | 2 + src/openvpn/fuzz.h | 47 ++ src/openvpn/fuzz_header.h | 79 +++ src/openvpn/misc.c | 10 + src/openvpn/mroute.c | 2 + src/openvpn/openvpn.c | 4 + src/openvpn/packet_id.c | 4 + src/openvpn/proxy.c | 20 + src/openvpn/socket.c | 4 + src/openvpn/socket.h | 9 + 33 files changed, 3162 insertions(+) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/README.md create mode 100755 fuzz/openvpn-fuzz.py create mode 100644 fuzz/shell.nix create mode 100644 fuzz/src/fuzz_base64.c create mode 100644 fuzz/src/fuzz_buffer.c create mode 100644 fuzz/src/fuzz_dhcp.c create mode 100644 fuzz/src/fuzz_forward.c create mode 100644 fuzz/src/fuzz_list.c create mode 100644 fuzz/src/fuzz_misc.c create mode 100644 fuzz/src/fuzz_mroute.c create mode 100644 fuzz/src/fuzz_mss.c create mode 100644 fuzz/src/fuzz_packet_id.c create mode 100644 fuzz/src/fuzz_parse_argv.c create mode 100644 fuzz/src/fuzz_proxy.c create mode 100644 fuzz/src/fuzz_randomizer.cpp create mode 100644 fuzz/src/fuzz_randomizer.h create mode 100644 fuzz/src/fuzz_route.c create mode 100644 fuzz/src/fuzz_verify_cert.c create mode 100644 fuzz/src/fuzz_verify_cert.h create mode 100644 src/openvpn/fake_fuzz_header.h create mode 100644 src/openvpn/fuzz.h create mode 100644 src/openvpn/fuzz_header.h diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 00000000000..641b59d91dc --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,2 @@ +build +corpus diff --git a/fuzz/README.md b/fuzz/README.md new file mode 100644 index 00000000000..247a3c0a929 --- /dev/null +++ b/fuzz/README.md @@ -0,0 +1,19 @@ +# OpenVPN Fuzzing + +## Setup +The fuzzing setup is handled by Nix inside a `nix-shell` and works both on +Linux and macOS. Nix is the only dependency (https://nixos.org/download.html). + +## Usage + +```sh +$ nix-shell fuzz/shell.nix +$ autoreconf -i -v -f +$ ./configure --disable-lz4 +$ cd fuzz +$ ./openvpn-fuzz.py fuzz base64 +$ ./openvpn-fuzz.py fuzz parse_argv -- -fork=4 -ignore_crashes=1 +$ ./openvpn-fuzz.py coverage base64 parse_argv # specified targets +$ ./openvpn-fuzz.py coverage # all targets +$ ./openvpn-fuzz.py coverage --clean # do make clean before and after, use if previously built for fuzzing +``` diff --git a/fuzz/openvpn-fuzz.py b/fuzz/openvpn-fuzz.py new file mode 100755 index 00000000000..d22062e363d --- /dev/null +++ b/fuzz/openvpn-fuzz.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python + +import argparse +import os +import platform +import subprocess +import sys + +TARGETS = [ + 'base64', + 'buffer', + 'dhcp', + 'forward', + 'list', + 'misc', + 'mroute', + 'mss', + 'packet_id', + 'parse_argv', + 'proxy', + 'route', + 'verify_cert', + ] + +BASE_DIR = os.path.dirname(os.path.realpath(__file__)) + +def fuzz_target(target, args=[]): + build_targets([target]) + os.makedirs(corpus_dir(target), exist_ok=True) + os.chdir(target_dir(target, "fuzzer")) + os.execv(target_bin_path(target, "fuzzer"), + [target_bin_path(target, "fuzzer"), corpus_dir(target)] + args) + +def generate_coverage_report(targets=TARGETS): + """ + If OpenVPN was previously built for fuzzing run `make -C ../ clean` before and after generating coverage. + """ + wd = os.getcwd() + build_targets(targets, for_coverage=True) + profraws = [] + object_args = [] + for target in targets: + os.chdir(target_dir(target, "coverage")) + profraws.append(target_dir(target, "coverage", "default.profraw")) + object_args.append("-object") + object_args.append(target_bin_path(target, "coverage")) + subprocess.run([target_bin_path(target, "coverage"), corpus_dir(target), "-runs=0"]) + + profdata = build_dir("coverage", "combined.profdata") + subprocess.run(["llvm-profdata", "merge", "-o", profdata, "-sparse"] + profraws) + subprocess.run(["llvm-cov", "show", "--format", "html", f"-instr-profile={profdata}", + "-output-dir", build_dir("coverage", "report")] + object_args) + os.chdir(wd) + +def triage_parse_argv_crashes(): + """ + Filters out false positives that are caused by calling exit. + """ + import pwn + target = "parse_argv" + for filename in os.listdir(target_dir(target, "fuzzer")): + if "crash-" in filename: + print("Triaging", filename) + with open(target_dir(target, "fuzzer", filename), "rb") as f: + argv_raw = f.read() + p = pwn.process(executable="../src/openvpn/openvpn", argv=argv_raw.split(b'\x00')) + out = p.readall() + if b"SIGSEGV" in out or b"smashing" or b"AddressSanitizer" in out: + print(pwn.hexdump(argv_raw)) + print(out) + exit(1) + p.close() + +def build_openvpn(cflags): + """ + Build OpenVPN as usual, assumes `autoconf -f -v -f` and `./configure --disable-lz4` already run. + """ + subprocess.run(["make", "-j", "-C", "../", f"CFLAGS={cflags}"]) + +def build_targets(targets, for_coverage=False): + fuzzer_flags = '-g -fsanitize=address,fuzzer-no-link' + coverage_flags = '-g -fprofile-instr-generate -fcoverage-mapping' + + build_subdir = 'coverage' if for_coverage else 'fuzzer' + os.makedirs(build_dir(build_subdir), exist_ok=True) + + cflags = coverage_flags if for_coverage else fuzzer_flags + cflags += " -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION" + build_openvpn(cflags) + + o_files = [] + for file in os.listdir("../src/openvpn"): + if file.endswith(".o") and file != 'openvpn.o': + o_files.append("../src/openvpn/" + file) + subprocess.run(["ar", "r", build_dir(build_subdir, "libopenvpn.a")] + o_files) + + subprocess.run(["clang++", "-c", "src/fuzz_randomizer.cpp", + "-o", build_dir(build_subdir, "fuzz_randomizer.o")] + + cflags.split(' ')) + + extra_libs = ["-lc++abi", "-lresolv"] if platform.system() == 'Darwin' else ['-lcap-ng'] + + for target in targets: + os.makedirs(target_dir(target, build_subdir), exist_ok=True) + subprocess.run(["clang", "-I../src/openvpn", "-I..", "-I../src/compat", "-I../include", + "-lssl", "-lcrypto", "-llzo2", f"src/fuzz_{target}.c", + build_dir(build_subdir, "libopenvpn.a"), + build_dir(build_subdir, "fuzz_randomizer.o"), + "-o", target_bin_path(target, build_subdir), + "-g", "-fsanitize=address,fuzzer"] + + (coverage_flags.split(' ') if for_coverage else []) + + extra_libs) + +def build_dir(subdir, file=''): + """ + There are two build flavors that live in their own subdirs: coverage and fuzzer. + """ + return os.path.join(BASE_DIR, "build", subdir, file) + +def target_dir(target, subdir, file=''): + return os.path.join(build_dir(subdir), f"fuzz_{target}", file) + +def corpus_dir(target): + return os.path.join(BASE_DIR, "corpus", f"fuzz_{target}") + +def target_bin(target): + return f"fuzz_{target}" + +def target_bin_path(target, subdir): + return target_dir(target, subdir, target_bin(target)) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="subcommand") + fuzz_parser = subparsers.add_parser('fuzz') + fuzz_parser.add_argument('target', type=str) + fuzz_parser.add_argument('libfuzzer_args', type=str, nargs='*') + coverage_parser = subparsers.add_parser('coverage') + coverage_parser.add_argument('targets', type=str, nargs='*') + coverage_parser.add_argument('--clean', action=argparse.BooleanOptionalAction) + + args = parser.parse_args() + if args.subcommand == 'fuzz': + # ./openvpn-fuzz.py fuzz proxy -- -fork=4 -ignore_crashes=1 + fuzz_target(args.target, args.libfuzzer_args) + elif args.subcommand == 'coverage': + if args.clean: + subprocess.run(["make", "-C", "../", "clean"]) + + if args.targets: + generate_coverage_report(args.targets) + else: + generate_coverage_report() + + if args.clean: + subprocess.run(["make", "-C", "../", "clean"]) + else: + parser.print_help() diff --git a/fuzz/shell.nix b/fuzz/shell.nix new file mode 100644 index 00000000000..e689d3d7a57 --- /dev/null +++ b/fuzz/shell.nix @@ -0,0 +1,19 @@ +with import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/e58a7747db96c23b8a977e7c1bbfc5753b81b6fa.tar.gz") {}; + +let llvmPackages = llvmPackages_14; +in llvmPackages.stdenv.mkDerivation { + name = "openvpn-fuzz"; + buildInputs = [ + autoconf + automake + m4 + libtool + pkg-config + openssl_1_1 + lz4 + lzo + pam + llvmPackages.llvm + python3Packages.pwntools + ] ++ lib.optional (!stdenv.isDarwin) libcap_ng; +} diff --git a/fuzz/src/fuzz_base64.c b/fuzz/src/fuzz_base64.c new file mode 100644 index 00000000000..c9c45b84b36 --- /dev/null +++ b/fuzz/src/fuzz_base64.c @@ -0,0 +1,43 @@ +/* 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 "base64.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 500) { + return 0; + } + + char *new_str = (char *)malloc(size + 1); + if (new_str == NULL) { + return 0; + } + memcpy(new_str, data, size); + new_str[size] = '\0'; + + char *str = NULL; + openvpn_base64_encode(data, size, &str); + if(str != NULL) { + free(str); + } + + uint16_t outsize = 10000; + char *output_buf = (char *)malloc(outsize); + openvpn_base64_decode(new_str, output_buf, outsize); + free(output_buf); + + free(new_str); + return 0; +} diff --git a/fuzz/src/fuzz_buffer.c b/fuzz/src/fuzz_buffer.c new file mode 100644 index 00000000000..4062c41f35f --- /dev/null +++ b/fuzz/src/fuzz_buffer.c @@ -0,0 +1,273 @@ +/* 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 "config.h" +#include "syshead.h" +#include "misc.h" +#include "buffer.h" + +#include "fuzz_randomizer.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzz_random_init(data,size); + + struct gc_arena gc; + struct buffer *bufp; + struct buffer buf, buf2; + struct buffer_list *buflistp = NULL; + ssize_t generic_ssizet, _size; + char *tmp; + char *tmp2; + char match; + + gc = gc_new(); + bufp = NULL; + + int total_to_fuzz = fuzz_randomizer_get_int(1, 20); + for (int i = 0; i < total_to_fuzz; i++) { + if (bufp == NULL) { + generic_ssizet = fuzz_randomizer_get_byte(0, 1); + if (generic_ssizet == -1) goto cleanup; + if (generic_ssizet == 0) { + _size = fuzz_randomizer_get_byte(0, 100); + if (_size == -1) goto cleanup; + buf = alloc_buf_gc(_size, &gc); + bufp = &buf; + } else { + tmp = get_random_string(); + buf = string_alloc_buf(tmp, &gc); + bufp = &buf; + free(tmp); + tmp = NULL; + } + } else { +#define NUM_TARGETS 32 + generic_ssizet = fuzz_randomizer_get_byte(0, NUM_TARGETS); + if (generic_ssizet == -1) goto cleanup; + switch (generic_ssizet) { + case 0: + buf_clear(bufp); + break; + case 1: + buf2 = clone_buf(bufp); + free_buf(&buf2); + break; + case 2: + buf_defined(bufp); + break; + case 3: + buf_valid(bufp); + break; + case 4: + buf_bptr(bufp); + break; + case 5: + buf_len(bufp); + break; + case 6: + buf_bend(bufp); + break; + case 7: + buf_blast(bufp); + break; + case 8: + buf_str(bufp); + break; + case 9: + generic_ssizet = fuzz_randomizer_get_byte(0, 255); + if (generic_ssizet == -1) goto cleanup; + buf_rmtail(bufp, (uint8_t)generic_ssizet); + break; + case 10: + buf_chomp(bufp); + break; + case 11: + tmp = get_random_string(); + skip_leading_whitespace(tmp); + free(tmp); + tmp = NULL; + break; + case 12: + tmp = get_random_string(); + chomp(tmp); + free(tmp); + tmp = NULL; + break; + case 13: + tmp = get_random_string(); + tmp2 = get_random_string(); + rm_trailing_chars(tmp, tmp2); + free(tmp); + free(tmp2); + tmp = NULL; + tmp2 = NULL; + break; + case 14: + tmp = get_random_string(); + string_clear(tmp); + free(tmp); + tmp = NULL; + break; + case 15: + tmp = get_random_string(); + buf_string_match_head_str(bufp, tmp); + free(tmp); + tmp = NULL; + break; + case 16: + tmp = get_random_string(); + buf_string_compare_advance(bufp, tmp); + free(tmp); + tmp = NULL; + break; + case 17: + generic_ssizet = fuzz_randomizer_get_byte(0, 255); + if (generic_ssizet == -1) goto cleanup; + + tmp = get_random_string(); + if (strlen(tmp) > 0) { + buf_parse(bufp, (int)generic_ssizet, tmp, strlen(tmp)); + } + + free(tmp); + tmp = NULL; + break; + case 18: + tmp = get_random_string(); + string_mod(tmp, fuzz_randomizer_get_int(0, 12312), + fuzz_randomizer_get_int(0, 23141234), + (char)fuzz_randomizer_get_int(0, 255)); + + free(tmp); + tmp = NULL; + break; + case 19: + tmp = get_random_string(); + match = (char)fuzz_randomizer_get_int(0, 255); + if (match != 0) { + string_replace_leading(tmp, match, (char)fuzz_randomizer_get_int(0, 255)); + } + + free(tmp); + tmp = NULL; + break; + case 20: + tmp = get_random_string(); + buf_write(bufp, tmp, strlen(tmp)); + + free(tmp); + tmp = NULL; + break; + case 21: + tmp = get_random_string(); + + buf_write_prepend(bufp, tmp, strlen(tmp)); + + free(tmp); + tmp = NULL; + break; + case 22: + buf_write_u8(bufp, fuzz_randomizer_get_int(0, 255)); + break; + case 23: + buf_write_u16(bufp, fuzz_randomizer_get_int(0, 1024)); + break; + case 24: + buf_write_u32(bufp, fuzz_randomizer_get_int(0, 12312)); + break; + case 25: + tmp = get_random_string(); + buf_catrunc(bufp, tmp); + free(tmp); + tmp = NULL; + break; + case 26: + convert_to_one_line(bufp); + break; + case 27: + buf_advance(bufp, fuzz_randomizer_get_int(0, 25523)); + break; + case 28: + buf_prepend(bufp, fuzz_randomizer_get_int(0, 251235)); + break; + case 29: + buf_reverse_capacity(bufp); + break; + case 30: + buf_forward_capacity_total(bufp); + break; + case 31: + buf_forward_capacity(bufp); + break; + case 32: + tmp = get_random_string(); + buf_puts(bufp, tmp); + free(tmp); + tmp = NULL; + break; + } + } + + if (buflistp == NULL) { + buflistp = buffer_list_new(); + } else { +#define NUM_LIST_TARGETS 6 + generic_ssizet = fuzz_randomizer_get_byte(0, NUM_LIST_TARGETS); + if (generic_ssizet == -1) goto cleanup; + switch (generic_ssizet) { + case 0: + buffer_list_free(buflistp); + buflistp = NULL; + break; + case 1: + buffer_list_defined(buflistp); + break; + case 2: + tmp = get_random_string(); + if (strlen(tmp) < BUF_SIZE_MAX) { + buffer_list_push(buflistp, tmp); + } + free(tmp); + tmp = NULL; + break; + case 3: + buffer_list_peek(buflistp); + break; + case 4: + buffer_list_pop(buflistp); + break; + case 5: + tmp = get_random_string(); + buffer_list_aggregate_separator( + buflistp, fuzz_randomizer_get_int(0, 1024), tmp); + + free(tmp); + tmp = NULL; + break; + case 6: + buffer_list_aggregate(buflistp, + fuzz_randomizer_get_int(0, 1024)); + break; + } + } + } + +cleanup: + // Cleanup + buffer_list_free(buflistp); + gc_free(&gc); + + fuzz_random_destroy(); + + return 0; +} diff --git a/fuzz/src/fuzz_dhcp.c b/fuzz/src/fuzz_dhcp.c new file mode 100644 index 00000000000..1368ac2a138 --- /dev/null +++ b/fuzz/src/fuzz_dhcp.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 "config.h" +#include "syshead.h" +#include "dhcp.h" +#include "buffer.h" + +#include "fuzz_randomizer.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + struct buffer ipbuf; + in_addr_t ret; + + fuzz_random_init(data, size); + char *ran_val = get_random_string(); + + ipbuf = alloc_buf(strlen(ran_val)); + if (buf_write(&ipbuf, ran_val, strlen(ran_val)) != false) { + ret = dhcp_extract_router_msg(&ipbuf); + } + free_buf(&ipbuf); + + fuzz_random_destroy(); + free(ran_val); + + return 0; +} diff --git a/fuzz/src/fuzz_forward.c b/fuzz/src/fuzz_forward.c new file mode 100644 index 00000000000..954f2d0608a --- /dev/null +++ b/fuzz/src/fuzz_forward.c @@ -0,0 +1,227 @@ +/* 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 "config.h" +#include +#include "syshead.h" +#include "interval.h" +#include "init.h" +#include "buffer.h" +#include "forward.h" + +#include "fuzz_randomizer.h" + +static int init_c2_outgoing_link(struct context_2 *c2, struct gc_arena *gc) { + struct link_socket_actual *to_link_addr = NULL; + struct link_socket *link_socket = NULL; + struct socks_proxy_info *socks_proxy = NULL; + struct buffer buf; + + c2->tun_write_bytes = 0; + ALLOC_ARRAY_GC(link_socket, struct link_socket, 1, gc); + memset(link_socket, 0, sizeof(*link_socket)); + + c2->link_socket = link_socket; + + if (fuzz_randomizer_get_int(0, 2) != 0) { + c2->link_socket->info.proto = PROTO_UDP; + } else { + c2->link_socket->info.proto = PROTO_TCP_SERVER; + } + + ALLOC_ARRAY_GC(socks_proxy, struct socks_proxy_info, 1, gc); + memset(socks_proxy, 0, sizeof(*socks_proxy)); + c2->link_socket->socks_proxy = socks_proxy; + + //c2->frame.link_mtu_dynamic = fuzz_randomizer_get_int(0, 0xfffffff); + //c2->frame.extra_frame = fuzz_randomizer_get_int(0, 0xfffffff); + c2->frame.extra_tun = fuzz_randomizer_get_int(0, 0xfffffff); + //c2->frame.link_mtu = fuzz_randomizer_get_int(0, 0xfffffff); + + ALLOC_ARRAY_GC(to_link_addr, struct link_socket_actual, 1, gc); + memset(to_link_addr, 0, sizeof(*to_link_addr)); + c2->to_link_addr = to_link_addr; + + c2->to_link_addr->dest.addr.sa.sa_family = AF_INET; + c2->to_link_addr->dest.addr.in4.sin_addr.s_addr = 1; + + char *tmp = get_random_string(); + buf = alloc_buf_gc(strlen(tmp), gc); + buf_write(&buf, tmp, strlen(tmp)); + int val = fuzz_randomizer_get_int(0, strlen(tmp)); + buf.offset = val; + free(tmp); + + c2->link_socket->stream_buf.maxlen = BLEN(&buf); + c2->to_link = buf; + + if (buf.offset < 10) { + return -1; + } + return 0; +} + +void fuzz_process_outgoing_link(const uint8_t *data, size_t size) { + struct context ctx; + struct gc_arena gc = gc_new(); + memset(&ctx, 0, sizeof(ctx)); + + if (init_c2_outgoing_link(&ctx.c2, &gc) == 0) { + process_outgoing_link(&ctx); + } + + gc_free(&gc); +} + +static int _init_options(struct options *options, struct client_nat_entry **cne, + struct gc_arena *gc) { + options->passtos = false; + options->mode = MODE_POINT_TO_POINT; + options->allow_recursive_routing = true; + options->client_nat = new_client_nat_list(gc); + + struct client_nat_entry *_cne; + ALLOC_ARRAY_GC(cne[0], struct client_nat_entry, 1, gc); + _cne = cne[0]; + memset(_cne, 0, sizeof(struct client_nat_entry)); + + struct client_nat_option_list clist; + clist.n = 1; + clist.entries[0] = *_cne; + copy_client_nat_option_list(options->client_nat, &clist); + options->route_gateway_via_dhcp = false; + + return 0; +} + +static int init_c2_incoming_tun(struct context_2 *c2, struct gc_arena *gc) { + struct buffer buf; + memset(&buf, 0, sizeof(buf)); + + struct link_socket *link_socket = NULL; + ALLOC_ARRAY_GC(link_socket, struct link_socket, 1, gc); + c2->link_socket = link_socket; + + ALLOC_OBJ_GC(c2->link_socket_info, struct link_socket_info, gc); + ALLOC_OBJ_GC(c2->link_socket_info->lsa, struct link_socket_addr, gc); + c2->link_socket_info->lsa->bind_local = NULL; + c2->link_socket_info->lsa->remote_list = NULL; + c2->link_socket_info->lsa->current_remote = NULL; + c2->link_socket_info->lsa->remote_list = NULL; + c2->es = env_set_create(gc); + + //c2->frame.link_mtu_dynamic = 0; + //c2->frame.extra_frame = 0; + c2->frame.extra_tun = 0; + c2->to_link_addr = NULL; + + char *tmp = get_random_string(); + buf = alloc_buf(strlen(tmp)); + buf_write(&buf, tmp, strlen(tmp)); + + int retval; + if (strlen(tmp) > 5) { + retval = 0; + } else { + retval = 1; + } + + free(tmp); + + c2->buf = buf; + c2->buffers = init_context_buffers(&c2->frame); + c2->log_rw = false; + + return retval; +} + +int run_process_incoming_tun(const uint8_t *data, size_t size) { + struct gc_arena gc; + struct context ctx; + struct client_nat_entry *cne[MAX_CLIENT_NAT]; + struct route_list route_list; + + memset(&ctx, 0, sizeof(ctx)); + memset(cne, 0, sizeof(cne)); + + gc = gc_new(); + + _init_options(&ctx.options, cne, &gc); + + // Init tuntap + struct tuntap tuntap; + tuntap.type = DEV_TYPE_TAP; + + ctx.c1.tuntap = &tuntap; + + int retval = init_c2_incoming_tun(&ctx.c2, &gc); + ctx.c1.route_list = &route_list; + if (retval == 0) { + process_incoming_tun(&ctx); + } + + free(ctx.c2.buf.data); + free_context_buffers(ctx.c2.buffers); + gc_free(&gc); +} + +static int init_c2_outgoing_tun(struct context_2 *c2, struct gc_arena *gc) { + struct buffer buf; + + c2->tun_write_bytes = 0; + c2->frame.extra_tun = fuzz_randomizer_get_int(0, 0xfffffff); + + char *tmp = get_random_string(); + buf = alloc_buf_gc(strlen(tmp), gc); + buf_write(&buf, tmp, strlen(tmp)); + free(tmp); + + c2->to_tun = buf; + return 0; +} + +void run_process_outgoing_tun(uint8_t *data, size_t size) { + struct gc_arena gc; + struct context ctx; + struct tuntap tuntap; + + memset(&ctx, 0, sizeof(ctx)); + gc = gc_new(); + + tuntap.type = DEV_TYPE_TAP; + ctx.c1.tuntap = &tuntap; + + init_c2_outgoing_tun(&ctx.c2, &gc); + process_outgoing_tun(&ctx); + + gc_free(&gc); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzz_random_init(data, size); + + int dec = fuzz_randomizer_get_byte(0, 2); + if (dec == -1) goto cleanup; + if (dec == 0) { + run_process_incoming_tun(data, size); + } + else if (dec == 1) { + run_process_outgoing_tun(data, size); + } + else { + fuzz_process_outgoing_link(data, size); + } + +cleanup: + fuzz_random_destroy(); + return 0; +} diff --git a/fuzz/src/fuzz_list.c b/fuzz/src/fuzz_list.c new file mode 100644 index 00000000000..d4bd579e547 --- /dev/null +++ b/fuzz/src/fuzz_list.c @@ -0,0 +1,137 @@ +/* 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 "config.h" +#include "syshead.h" +#include "list.h" + +#include "fuzz_randomizer.h" + +#define KEY_SIZE 23 + +/* Required for hash_init() */ +static uint32_t word_hash_function(const void *key, uint32_t iv) { + return hash_func(key, KEY_SIZE, iv); +} + +/* Required for hash_init() */ +static bool word_compare_function(const void *key1, const void *key2) { + return ((size_t)key1 & 0xFFF) == ((size_t)key1 & 0xFFF); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + struct gc_arena gc; + struct hash *hash = NULL; + ssize_t generic_ssizet, generic_ssizet2, num_loops; + + fuzz_random_init(data, size); + + gc = gc_new(); + + int total_to_fuzz = fuzz_randomizer_get_int(1, 20); + for (int i = 0; i < total_to_fuzz; i++) { + generic_ssizet = fuzz_randomizer_get_byte(0, 8); + if (generic_ssizet == -1) goto cleanup; + + switch (generic_ssizet) { + case 0: + if (hash == NULL) { + int n_buckets = fuzz_randomizer_get_int(1, 1000); + uint32_t iv; + + hash = + hash_init(n_buckets, iv, word_hash_function, word_compare_function); + } + break; + case 1: + if (hash) { + hash_free(hash); + hash = NULL; + } + break; + case 2: + if (hash) { + struct hash_iterator hi; + struct hash_element *he; + hash_iterator_init(hash, &hi); + while ((he = hash_iterator_next(&hi))) { + void *w = he->value; + } + hash_iterator_free(&hi); + } + break; + case 3: + if (hash) { + void *key; + void *value; + char arr[KEY_SIZE]; + memset(arr, 0, KEY_SIZE); + fuzz_get_random_data(arr, KEY_SIZE); + key = (void *)arr; + if (!hash_lookup(hash, key)) { + generic_ssizet = fuzz_randomizer_get_int(0, 0xfffffff); + value = (void *)generic_ssizet; + hash_add(hash, key, value, false); + } + } + break; + case 4: + if (hash) { + hash_n_elements(hash); + } + break; + case 5: + if (hash) { + hash_n_buckets(hash); + } + break; + case 6: + if (hash) { + uint32_t hv; + generic_ssizet = fuzz_randomizer_get_int(0, 0xfffffff); + hv = generic_ssizet; + hash_bucket(hash, hv); + } + break; + case 7: + if (hash) { + void *key; + char arr[KEY_SIZE]; + memset(arr, 0, KEY_SIZE); + fuzz_get_random_data(arr, KEY_SIZE); + key = (void *)arr; + hash_remove(hash, key); + } + break; + case 8: + if (hash) { + void *value; + generic_ssizet = fuzz_randomizer_get_int(0, 0xfffffff); + value = (void *)generic_ssizet; + hash_remove_by_value(hash, value); + } + default: + break; + } + } + +cleanup: + if (hash) { + hash_free(hash); + } + + gc_free(&gc); + + fuzz_random_destroy(); + + return 0; +} diff --git a/fuzz/src/fuzz_misc.c b/fuzz/src/fuzz_misc.c new file mode 100644 index 00000000000..5485355d18a --- /dev/null +++ b/fuzz/src/fuzz_misc.c @@ -0,0 +1,79 @@ +/* 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 "config.h" +#include "syshead.h" +#include "misc.h" +#include "buffer.h" + +#include "fuzz_randomizer.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzz_random_init(data, size); + + struct gc_arena gc; + struct env_set *es; + gc = gc_new(); + es = env_set_create(&gc); + + int total_to_fuzz = fuzz_randomizer_get_int(1, 9); + for (int i = 0; i 1 && strlen(tmp2) > 1) { + setenv_str(es, tmp2, tmp1); + } + break; + case 4: + hostname_randomize(tmp1, &gc); + break; + case 5: + if (strlen(tmp1) > 0) { + get_auth_challenge(tmp1, &gc); + } + break; + case 6: + output_peer_info_env(es, tmp1); + break; + case 7: + sanitize_control_message(tmp1, &gc); + break; + case 8: + prepend_dir(tmp1, tmp2, &gc); + break; + } + free(tmp1); + free(tmp2); + } + +cleanup: + env_set_destroy(es); + gc_free(&gc); + + fuzz_random_destroy(); + return 0; +} diff --git a/fuzz/src/fuzz_mroute.c b/fuzz/src/fuzz_mroute.c new file mode 100644 index 00000000000..63d0ce23dd5 --- /dev/null +++ b/fuzz/src/fuzz_mroute.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 "config.h" +#include "syshead.h" +#include "init.h" +#include "mroute.h" + +#include "fuzz_randomizer.h" + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + fuzz_random_init(data, size); + struct buffer buf; + struct gc_arena gc; + + gc = gc_new(); + + char *tmp = get_random_string(); + buf = string_alloc_buf(tmp, &gc); + free(tmp); + + struct mroute_addr src_addr; + struct mroute_addr dst_addr; + mroute_addr_init(&src_addr); + mroute_addr_init(&dst_addr); + unsigned int ret = mroute_extract_addr_ip(&src_addr, &dst_addr, &buf); + + if (ret & MROUTE_EXTRACT_SUCCEEDED) { + mroute_addr_mask_host_bits(&src_addr); + mroute_addr_print(&src_addr, &gc); + mroute_learnable_address(&src_addr, &gc); + } + + uint16_t vid; + struct mroute_addr a1, a2; + mroute_addr_init(&a1); + mroute_addr_init(&a2); + mroute_extract_addr_ether(&a1, &a2, vid, &buf); + + if (size > sizeof(struct openvpn_sockaddr)) { + struct openvpn_sockaddr local_sock; + memcpy(&local_sock, data, sizeof(struct openvpn_sockaddr)); + mroute_extract_openvpn_sockaddr(&a1, &local_sock, true); + mroute_extract_openvpn_sockaddr(&a1, &local_sock, false); + } + + struct mroute_helper *mhelper = NULL; + mhelper = mroute_helper_init(fuzz_randomizer_get_int(0, 0xfffffff)); + if (mhelper != NULL) { + mroute_helper_add_iroute46(mhelper, fuzz_randomizer_get_int(0, MR_HELPER_NET_LEN-1)); + mroute_helper_free(mhelper); + } + + gc_free(&gc); + + fuzz_random_destroy(); + return 0; +} + diff --git a/fuzz/src/fuzz_mss.c b/fuzz/src/fuzz_mss.c new file mode 100644 index 00000000000..cf143823989 --- /dev/null +++ b/fuzz/src/fuzz_mss.c @@ -0,0 +1,28 @@ +#include "config.h" +#include "syshead.h" + +#include "buffer.h" +#include "mss.h" +#include "fuzz_randomizer.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzz_random_init(data, size); + + struct buffer buf; + + int src_len = fuzz_randomizer_get_int(0, 4096); + int maxmss = fuzz_randomizer_get_int(0, 4096); + + buf = alloc_buf(size); + char* src = malloc(src_len); + fuzz_get_random_data(src, src_len); + if (buf_write(&buf, src, src_len) != false) { + mss_fixup_ipv4(&buf, maxmss); + mss_fixup_ipv6(&buf, maxmss); + } + free_buf(&buf); + free(src); + + fuzz_random_destroy(); + return 0; +} diff --git a/fuzz/src/fuzz_packet_id.c b/fuzz/src/fuzz_packet_id.c new file mode 100644 index 00000000000..9ece9384e5f --- /dev/null +++ b/fuzz/src/fuzz_packet_id.c @@ -0,0 +1,105 @@ +/* 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 "config.h" +#include "syshead.h" +#include "init.h" +#include "packet_id.h" + +#include "fuzz_randomizer.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzz_random_init(data, size); + + struct packet_id pid; + struct packet_id_net pin; + const int seq_backtrack = 10; + const int time_backtrack = 10; + + packet_id_init(&pid, seq_backtrack, time_backtrack, "name", 0); + + int total_sends = fuzz_randomizer_get_byte(0, 10); + if (total_sends == -1) goto cleanup; + for (int i = 0; i < total_sends; i++) { + update_time(); + pin.time = fuzz_randomizer_get_int(0, 0xfffffff); + pin.id = fuzz_randomizer_get_int(0, 0xfffffff); + + packet_id_reap_test(&pid.rec); + bool test = packet_id_test(&pid.rec, &pin); + if (test) { + packet_id_add(&pid.rec, &pin); + } + } + packet_id_free(&pid); + + // packet id send + char *tmp2 = get_random_string(); + if (strlen(tmp2) > sizeof(struct packet_id_send)) { + struct packet_id_send pidsend; + memcmp(&pidsend, tmp2, sizeof(struct packet_id_send)); + + struct timeval tv; + tv.tv_sec = pidsend.time; + tv.tv_usec = 0; + if (localtime(&tv)) { + struct buffer iv_buffer; + buf_set_write(&iv_buffer, tmp2, strlen(tmp2)); + packet_id_write(&pidsend, &iv_buffer, false, false); + packet_id_write(&pidsend, &iv_buffer, false, true); + packet_id_write(&pidsend, &iv_buffer, true, true); + packet_id_write(&pidsend, &iv_buffer, true, false); + } + } + free(tmp2); + + struct gc_arena gc; + gc = gc_new(); + struct buffer buf; + char *tmp = get_random_string(); + buf = string_alloc_buf(tmp, &gc); + free(tmp); + packet_id_read(&pid, &buf, false); + packet_id_read(&pid, &buf, true); + gc_free(&gc); + + char filename[256]; + sprintf(filename, "/tmp/libfuzzer.%d", getpid()); + + FILE *fp = fopen(filename, "wb"); + if (!fp) { + return 0; + } + fwrite(data, size, 1, fp); + fclose(fp); + + struct packet_id_persist p; + memset(&p, 0, sizeof(struct packet_id_persist)); + packet_id_persist_init(&p); + packet_id_persist_load(&p, filename); + struct timeval tv; + tv.tv_sec = p.time; + tv.tv_usec = 0; + if (localtime(&tv) != NULL) { + gc = gc_new(); + p.id_last_written = fuzz_randomizer_get_int(0, 0xfffffff); + packet_id_persist_print(&p, &gc); + packet_id_persist_save(&p); + gc_free(&gc); + } + +cleanup: + packet_id_persist_close(&p); + + fuzz_random_destroy(); + return 0; +} diff --git a/fuzz/src/fuzz_parse_argv.c b/fuzz/src/fuzz_parse_argv.c new file mode 100644 index 00000000000..3f4db8bf9c6 --- /dev/null +++ b/fuzz/src/fuzz_parse_argv.c @@ -0,0 +1,47 @@ +#include "config.h" +#include "syshead.h" + +#include "buffer.h" +#include "options.h" + +#define N 100 + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 10) { return 0; } + if (data[size-1] != 0) { return 0; } + + struct gc_arena gc; + struct options options; + struct env_set *es; + + gc = gc_new(); + init_options(&options, false); + es = env_set_create(&gc); + + char* argv[N+1]; + memset(argv, 0, sizeof(argv)); + + int argv_pos = 0; + + int last_start = 0; + for (int i = 0; i < size; i++) { + if (argv_pos >= N) goto cleanup; + + if (data[i] == 0) { + if (last_start == i) goto cleanup; // don't want empty args + argv[argv_pos] = data+last_start; + last_start = i+1; + argv_pos++; + } + } + + if (argv_pos > 1) + //parse_argv(&options, argv_pos, argv, M_USAGE, OPT_P_DEFAULT, NULL, es); + parse_argv(&options, argv_pos, argv, M_NOPREFIX | M_OPTERR, OPT_P_DEFAULT, NULL, es); + +cleanup: + env_set_destroy(es); + gc_free(&gc); + + return 0; +} diff --git a/fuzz/src/fuzz_proxy.c b/fuzz/src/fuzz_proxy.c new file mode 100644 index 00000000000..66a5918b585 --- /dev/null +++ b/fuzz/src/fuzz_proxy.c @@ -0,0 +1,137 @@ +/* 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 "config.h" +#include +#include "syshead.h" +#include "interval.h" +#include "proxy.h" +#include +#include + +#include "fuzz_randomizer.h" + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + OPENSSL_malloc_init(); + SSL_library_init(); + ERR_load_crypto_strings(); + + OpenSSL_add_all_algorithms(); + OpenSSL_add_ssl_algorithms(); + OpenSSL_add_all_digests(); + + SSL_load_error_strings(); + return 1; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + char *tmp = NULL; + char *tmp2 = NULL; + + fuzz_random_init(data, size); + + struct gc_arena gc = gc_new(); + struct http_proxy_info pi; + ssize_t generic_ssizet; + int signal_received = 0; + struct buffer lookahead = alloc_buf(1024); + struct event_timeout evt; + + memset(&evt, 0, sizeof(struct event_timeout)); + memset(&pi, 0, sizeof(struct http_proxy_info)); + memset(&pi, 0, sizeof(pi)); + + generic_ssizet = 0; + char *fuzz_usrnm = fuzz_random_get_string_max_length(USER_PASS_LEN-1); + strcpy(pi.up.username, fuzz_usrnm); + if (strlen(pi.up.username) == 0) { + gc_free(&gc); + free_buf(&lookahead); + free(fuzz_usrnm); + fuzz_random_destroy(); + return 0; + } + + char *pswd = fuzz_random_get_string_max_length(USER_PASS_LEN-1); + strcpy(pi.up.password, pswd); + if (strlen(pi.up.password) == 0) { + gc_free(&gc); + free_buf(&lookahead); + + free(pswd); + free(fuzz_usrnm); + fuzz_random_destroy(); + return 0; + } + + generic_ssizet = fuzz_randomizer_get_byte(0, 4); + if (generic_ssizet == -1) goto cleanup; + switch (generic_ssizet) { + case 0: + pi.auth_method = HTTP_AUTH_NONE; + break; + case 1: + pi.auth_method = HTTP_AUTH_BASIC; + break; + case 2: + pi.auth_method = HTTP_AUTH_DIGEST; + break; + case 3: + pi.auth_method = HTTP_AUTH_NTLM; + break; + case 4: + pi.auth_method = HTTP_AUTH_NTLM2; + break; + } + + generic_ssizet = fuzz_randomizer_get_byte(0, 2); + if (generic_ssizet == -1) goto cleanup; + switch (generic_ssizet) { + case 0: + pi.options.auth_retry = PAR_NO; + break; + case 1: + pi.options.auth_retry = PAR_ALL; + break; + case 2: + pi.options.auth_retry = PAR_NCT; + break; + } + + char *tmp_authenticate = get_random_string(); + pi.proxy_authenticate = tmp_authenticate; + + tmp = get_random_string(); + pi.options.custom_headers[0].name = tmp; + tmp2 = get_random_string(); + pi.options.custom_headers[0].content = tmp2; + + establish_http_proxy_passthru(&pi, 0, "1.2.3.4", "777", &evt, &lookahead, + &signal_received); + +cleanup: + free(pi.proxy_authenticate); + gc_free(&gc); + free_buf(&lookahead); + + if (tmp != NULL) free(tmp); + if (tmp2 != NULL) free(tmp2); + + free(pswd); + free(fuzz_usrnm); + fuzz_random_destroy(); + + return 0; +} diff --git a/fuzz/src/fuzz_randomizer.cpp b/fuzz/src/fuzz_randomizer.cpp new file mode 100644 index 00000000000..0ffbf886847 --- /dev/null +++ b/fuzz/src/fuzz_randomizer.cpp @@ -0,0 +1,114 @@ +/* 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 + +FuzzedDataProvider *prov = NULL; + +extern "C" void fuzz_random_init(const uint8_t *data, size_t size) { + assert(prov == NULL); + prov = new FuzzedDataProvider(data, size); +} + +extern "C" void fuzz_random_destroy() { + assert(prov != NULL); + delete prov; + prov = NULL; +} + +extern "C" char *get_random_string() { + assert(prov != NULL); + + std::string s1 = prov->ConsumeRandomLengthString(); + char *tmp = (char *)malloc(s1.size() + 1); + memcpy(tmp, s1.c_str(), s1.size()); + tmp[s1.size()] = '\0'; + return tmp; +} + +extern "C" int fuzz_randomizer_get_int(int min, int max) { + assert(prov != NULL); + return prov->ConsumeIntegralInRange(min, max); +} + +extern "C" int fuzz_randomizer_get_byte(uint8_t min, uint8_t max) { + assert(prov != NULL); + if (prov->remaining_bytes() >= 1) + return prov->ConsumeIntegralInRange(min, max); + else return -1; +} + +extern "C" char *fuzz_random_get_string_max_length(int max_len) { + assert(prov != NULL); + + std::string s1 = prov->ConsumeBytesAsString( + prov->ConsumeIntegralInRange(1, max_len)); + char *tmp123 = (char*)malloc(s1.size()+1); + memcpy(tmp123, s1.c_str(), s1.size()); + tmp123[s1.size()] = '\0'; + + return tmp123; +} + +extern "C" size_t fuzz_get_random_data(void *buf, size_t len) { + assert(prov != NULL); + size_t ret_val; + char *cbuf = (char*)buf; + + if (prov->remaining_bytes() == 0) { + return -1; + } + + double prob = prov->ConsumeProbability(); + if (prob < 0.05) { + return 0; + } + + //if (len == 1) { + // ret_val = prov->ConsumeData(buf, 1); + // return ret_val; + //} + ret_val = prov->ConsumeData(buf, len); + return ret_val; +} + + +// Simple garbage collector +#define GB_SIZE 100 +void *pointer_arr[GB_SIZE]; +static int pointer_idx = 0; + +// If the garbage collector is used then this must be called as first thing +// during a fuzz run. +extern "C" void gb_init() { + pointer_idx = 0; + + for (int i = 0; i < GB_SIZE; i++) { + pointer_arr[i] = NULL; + } +} + +extern "C" void gb_cleanup() { + for(int i = 0; i < GB_SIZE; i++) { + if (pointer_arr[i] != NULL) { + free(pointer_arr[i]); + } + } +} + +extern "C" char *gb_get_random_string() { + char *tmp = get_random_string(); + pointer_arr[pointer_idx++] = (void*)tmp; + return tmp; +} + diff --git a/fuzz/src/fuzz_randomizer.h b/fuzz/src/fuzz_randomizer.h new file mode 100644 index 00000000000..da1f0098066 --- /dev/null +++ b/fuzz/src/fuzz_randomizer.h @@ -0,0 +1,28 @@ +/* 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 + +void fuzz_random_init(const uint8_t *data, size_t size); +void fuzz_random_destroy(); +char *get_random_string(); +int fuzz_randomizer_get_int(int min, int max); +int fuzz_randomizer_get_byte(uint8_t min, uint8_t max); +size_t fuzz_get_random_data(void *buf, size_t len); +char *fuzz_random_get_string_max_length(int max_len); + +void gb_init(); +void gb_cleanup(); +char *gb_get_random_string(); + +int fuzz_success; diff --git a/fuzz/src/fuzz_route.c b/fuzz/src/fuzz_route.c new file mode 100644 index 00000000000..73d7980b59b --- /dev/null +++ b/fuzz/src/fuzz_route.c @@ -0,0 +1,202 @@ +/* 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 "config.h" +#include "syshead.h" +#include "init.h" +#include "proxy.h" +#include "interval.h" +#include "route.h" +#include "buffer.h" + +#include "fuzz_randomizer.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + fuzz_random_init(data, size); + + gb_init(); + + struct route_option_list *opt; + struct route_list rl; + + int route_list_inited = 0; + int route_list_ipv6_inited = 0; + + struct context c; + memset(&c, 0, sizeof(struct context)); + gc_init(&c.gc); + c.es = env_set_create(&c.gc); + init_options(&c.options, true); + net_ctx_init(&c, &c.net_ctx); + init_verb_mute(&c, IVM_LEVEL_1); + + init_options_dev(&c.options); + + pre_setup(&c.options); + + setenv_settings(c.es, &c.options); + + ALLOC_OBJ_CLEAR_GC(c.options.connection_list, struct connection_list, + &c.options.gc); + context_init_1(&c); + + in_addr_t remote_host; + ssize_t default_metric; + + struct route_ipv6_list rl6; + struct route_ipv6_option_list *opt6; + + memset(&rl, 0, sizeof(rl)); + memset(&rl6, 0, sizeof(rl6)); + memset(&opt, 0, sizeof(opt)); + memset(&opt6, 0, sizeof(opt6)); + + opt6 = new_route_ipv6_option_list(&c.gc); + opt = new_route_option_list(&c.gc); + + int total_to_fuzz = fuzz_randomizer_get_int(1, 20); + for (int i = 0; i < total_to_fuzz; i++) { + int selector = fuzz_randomizer_get_byte(0, 13); + if (selector == -1) goto cleanup; + switch (selector) { + case 0: + if (route_list_inited == 0) { + const char *remote_endpoint = gb_get_random_string(); + memset(&rl, 0, sizeof(struct route_list)); + rl.flags = fuzz_randomizer_get_int(0, 0xffffff); + + init_route_list(&rl, opt, remote_endpoint, default_metric, remote_host, + c.es, &c); + route_list_inited = 1; + } + break; + case 1: + if (route_list_inited) { + in_addr_t addr; + route_list_add_vpn_gateway(&rl, c.es, addr); + } + break; + case 2: + if (route_list_inited && route_list_ipv6_inited) { + struct tuntap tt; + memset(&tt, 0, sizeof(tt)); + add_routes(&rl, &rl6, &tt, 0, c.es, &c); + } + break; + case 3: + if (route_list_inited) { + setenv_routes(c.es, &rl); + } + break; + case 4: + if (route_list_inited) { + struct route_ipv4 r; + struct route_option ro; + ro.network = gb_get_random_string(); + ro.netmask = gb_get_random_string(); + ro.gateway = gb_get_random_string(); + ro.metric = gb_get_random_string(); + ro.next = NULL; + + memset(&r, 0, sizeof(struct route_ipv4)); + r.option = &ro; + r.flags = RT_DEFINED; + add_route(&r, NULL, 0, NULL, c.es, &c); + } + break; + case 5: + if (route_list_inited) { + char *s1 = get_random_string(); + is_special_addr(s1); + free(s1); + } + break; + case 6: + if (route_list_ipv6_inited == 0) { + const char *remote_endpoint = gb_get_random_string(); + memset(&rl, 0, sizeof(struct route_list)); + struct in6_addr remote_host; + + rl6.rgi6.flags = fuzz_randomizer_get_int(0, 0xffffff); + fuzz_get_random_data(&rl6.rgi6.hwaddr, 6); + + char *t1 = gb_get_random_string(); + if (strlen(t1) > 16) { + memcpy(rl6.rgi6.iface, t1, 16); + } else { + memcpy(rl6.rgi6.iface, t1, strlen(t1)); + } + + init_route_ipv6_list(&rl6, opt6, remote_endpoint, 0, &remote_host, c.es, + &c); + route_list_ipv6_inited = 1; + } + break; + case 7: { + unsigned int flags; + struct route_ipv6 r6; + struct tuntap tt; + memset(&tt, 0, sizeof(tt)); + tt.actual_name = gb_get_random_string(); + r6.iface = gb_get_random_string(); + r6.flags = fuzz_randomizer_get_int(0, 0xfffff); + r6.netbits = fuzz_randomizer_get_int(0, 0xfffff); + r6.metric = fuzz_randomizer_get_int(0, 0xfffff); + + r6.next = NULL; + + add_route_ipv6(&r6, &tt, 0, c.es, &c); + } break; + case 8: + if (route_list_ipv6_inited && route_list_inited) { + delete_routes(&rl, &rl6, NULL, 0, c.es, &c); + route_list_ipv6_inited = 0; + route_list_inited = 0; + } + break; + case 9: + if (route_list_ipv6_inited) { + setenv_routes_ipv6(c.es, &rl6); + } + break; + case 10: { + add_route_ipv6_to_option_list(opt6, gb_get_random_string(), + gb_get_random_string(), + gb_get_random_string()); + } break; + case 11: { + print_route_options(opt, M_NONFATAL); + } break; + case 12: { + add_route_to_option_list(opt, gb_get_random_string(), + gb_get_random_string(), gb_get_random_string(), + gb_get_random_string()); + } break; + default: + break; + } + } + +cleanup: + if (route_list_inited) { + gc_free(&rl.gc); + } + env_set_destroy(c.es); + context_gc_free(&c); + + fuzz_random_destroy(); + + gb_cleanup(); + + return 0; +} diff --git a/fuzz/src/fuzz_verify_cert.c b/fuzz/src/fuzz_verify_cert.c new file mode 100644 index 00000000000..c73a6ac828c --- /dev/null +++ b/fuzz/src/fuzz_verify_cert.c @@ -0,0 +1,167 @@ +/* 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 "config.h" +#include "syshead.h" + +#include +#include +#include +#include + +#include "fuzz_verify_cert.h" +#include "misc.h" +#include "manage.h" +#include "otime.h" +#include "base64.h" +#include "ssl_verify.h" +#include "ssl_verify_backend.h" + +#include "fuzz_randomizer.h" + + +static int parse_x509(const uint8_t *data, size_t size, X509 **out) { + *out = d2i_X509(NULL, (const unsigned char **)&data, size); + if (*out == NULL) { + return -1; + } + + return 0; +} + + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + OPENSSL_malloc_init(); + SSL_library_init(); + ERR_load_crypto_strings(); + + OpenSSL_add_all_algorithms(); + OpenSSL_add_ssl_algorithms(); + + SSL_load_error_strings(); + return 1; +} + + +static int init_session_opt(struct tls_options **_opt, struct gc_arena *gc) { + ssize_t nid; + ssize_t generic_ssizet; + struct tls_options *opt; + int r; + + ALLOC_OBJ_GC(*_opt, struct tls_options, gc); + if (opt == NULL) { + return -1; + } + + opt = *_opt; + + memset(opt, 0xFE, sizeof(struct tls_options)); + + opt->es = env_set_create(gc); + opt->x509_username_field[0] = NULL; + opt->remote_cert_eku = NULL; + + /* Prevents failure if x509 sha1 hashes do not match */ + opt->verify_hash = NULL; + + /* Prevent attempt to run --tls-verify script */ + opt->verify_command = NULL; + + /* Do not verify against CRL file */ + opt->crl_file = NULL; + + /* Do not run --tls-verify plugins */ + opt->plugins = NULL; + + r = fuzz_randomizer_get_int(0, 1); + if (r == 0) { + opt->x509_username_field[0] = nidstrs[fuzz_randomizer_get_int(0, (sizeof(nidstrs)/sizeof(nidstrs[0])) - 1)]; + } + else { + opt->x509_username_field[0] = "ext:subjectAltName"; + } + opt->x509_username_field[1] = NULL; + + r = fuzz_randomizer_get_int(0, 2); + if (r == 0) + opt->ns_cert_type = NS_CERT_CHECK_CLIENT; + else if (r == 1) + opt->ns_cert_type = NS_CERT_CHECK_SERVER; + else + opt->ns_cert_type = NS_CERT_CHECK_NONE; + + opt->x509_track = NULL; + + r = fuzz_randomizer_get_int(0, 1); + if (r == 0) + opt->remote_cert_eku = NULL; + else + opt->remote_cert_eku = get_random_string(); + + return 0; +} + + +static int init_session(struct tls_session **_session, struct gc_arena *gc) { + struct tls_session *session; + + ALLOC_OBJ_GC(*_session, struct tls_session, gc); + if (*_session == NULL) { + return -1; + } + + session = *_session; + memset(session, 0xFE, sizeof(struct tls_session)); + + /* Accessed in set_common_name() */ + session->common_name = get_random_string();; + + /* Initialize the session->opt structure */ + if (init_session_opt(&(session->opt), gc) == -1) { + free(session->common_name); + return -1; + } + + /* Accessed in server_untrusted() */ + session->untrusted_addr.dest.addr.sa.sa_family = AF_UNSPEC; + + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzz_random_init(data, size); + + struct gc_arena gc; + struct tls_session *session = NULL; + X509 *x509 = NULL; + gc = gc_new(); + + if (parse_x509(data, size, &x509) == 0) { + if (init_session(&session, &gc) == 0) { + verify_cert(session, x509, 100); + if (session->opt->remote_cert_eku != NULL) { + free(session->opt->remote_cert_eku); + } + free(session->common_name); + } + + } + + X509_free(x509); + gc_free(&gc); + + fuzz_random_destroy(); + + return 0; +} diff --git a/fuzz/src/fuzz_verify_cert.h b/fuzz/src/fuzz_verify_cert.h new file mode 100644 index 00000000000..8a53669b32b --- /dev/null +++ b/fuzz/src/fuzz_verify_cert.h @@ -0,0 +1,1065 @@ +/* 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. +*/ + +static char* nidstrs[] = { +"AD_DVCS", +"AES-128-CBC", +"AES-128-CBC-HMAC-SHA1", +"AES-128-CBC-HMAC-SHA256", +"AES-128-CFB", +"AES-128-CFB1", +"AES-128-CFB8", +"AES-128-CTR", +"AES-128-ECB", +"AES-128-OCB", +"AES-128-OFB", +"AES-128-XTS", +"AES-192-CBC", +"AES-192-CBC-HMAC-SHA1", +"AES-192-CBC-HMAC-SHA256", +"AES-192-CFB", +"AES-192-CFB1", +"AES-192-CFB8", +"AES-192-CTR", +"AES-192-ECB", +"AES-192-OCB", +"AES-192-OFB", +"AES-256-CBC", +"AES-256-CBC-HMAC-SHA1", +"AES-256-CBC-HMAC-SHA256", +"AES-256-CFB", +"AES-256-CFB1", +"AES-256-CFB8", +"AES-256-CTR", +"AES-256-ECB", +"AES-256-OCB", +"AES-256-OFB", +"AES-256-XTS", +"AuthDSS", +"AuthECDSA", +"AuthGOST01", +"AuthGOST12", +"AuthNULL", +"AuthPSK", +"AuthRSA", +"AuthSRP", +"BF-CBC", +"BF-CFB", +"BF-ECB", +"BF-OFB", +"BLAKE2b512", +"BLAKE2s256", +"C", +"CAMELLIA-128-CBC", +"CAMELLIA-128-CCM", +"CAMELLIA-128-CFB", +"CAMELLIA-128-CFB1", +"CAMELLIA-128-CFB8", +"CAMELLIA-128-CMAC", +"CAMELLIA-128-CTR", +"CAMELLIA-128-ECB", +"CAMELLIA-128-GCM", +"CAMELLIA-128-OFB", +"CAMELLIA-192-CBC", +"CAMELLIA-192-CCM", +"CAMELLIA-192-CFB", +"CAMELLIA-192-CFB1", +"CAMELLIA-192-CFB8", +"CAMELLIA-192-CMAC", +"CAMELLIA-192-CTR", +"CAMELLIA-192-ECB", +"CAMELLIA-192-GCM", +"CAMELLIA-192-OFB", +"CAMELLIA-256-CBC", +"CAMELLIA-256-CCM", +"CAMELLIA-256-CFB", +"CAMELLIA-256-CFB1", +"CAMELLIA-256-CFB8", +"CAMELLIA-256-CMAC", +"CAMELLIA-256-CTR", +"CAMELLIA-256-ECB", +"CAMELLIA-256-GCM", +"CAMELLIA-256-OFB", +"CAST5-CBC", +"CAST5-CFB", +"CAST5-ECB", +"CAST5-OFB", +"CMAC", +"CN", +"CRLReason", +"CSPName", +"ChaCha20", +"ChaCha20-Poly1305", +"CrlID", +"DC", +"DES-CBC", +"DES-CDMF", +"DES-CFB", +"DES-CFB1", +"DES-CFB8", +"DES-ECB", +"DES-EDE", +"DES-EDE-CBC", +"DES-EDE-CFB", +"DES-EDE-OFB", +"DES-EDE3", +"DES-EDE3-CBC", +"DES-EDE3-CFB", +"DES-EDE3-CFB1", +"DES-EDE3-CFB8", +"DES-EDE3-OFB", +"DES-OFB", +"DESX-CBC", +"DOD", +"DSA", +"DSA-SHA", +"DSA-SHA1", +"DSA-SHA1-old", +"DSA-old", +"DVCS", +"GN", +"HKDF", +"HMAC", +"HMAC-MD5", +"HMAC-SHA1", +"IANA", +"IDEA-CBC", +"IDEA-CFB", +"IDEA-ECB", +"IDEA-OFB", +"INN", +"ISO", +"ISO-US", +"ITU-T", +"JOINT-ISO-ITU-T", +"KISA", +"KxDHE", +"KxDHE-PSK", +"KxECDHE", +"KxECDHE-PSK", +"KxGOST", +"KxPSK", +"KxRSA", +"KxRSA_PSK", +"KxSRP", +"L", +"LocalKeySet", +"MD2", +"MD4", +"MD5", +"MD5-SHA1", +"MDC2", +"MGF1", +"Mail", +"NULL", +"Netscape", +"Nonce", +"O", +"OCSP", +"OCSPSigning", +"OGRN", +"ORG", +"OU", +"Oakley-EC2N-3", +"Oakley-EC2N-4", +"PBE-MD2-DES", +"PBE-MD2-RC2-64", +"PBE-MD5-DES", +"PBE-MD5-RC2-64", +"PBE-SHA1-2DES", +"PBE-SHA1-3DES", +"PBE-SHA1-DES", +"PBE-SHA1-RC2-128", +"PBE-SHA1-RC2-40", +"PBE-SHA1-RC2-64", +"PBE-SHA1-RC4-128", +"PBE-SHA1-RC4-40", +"PBES2", +"PBKDF2", +"PBMAC1", +"PKIX", +"PSPECIFIED", +"RC2-40-CBC", +"RC2-64-CBC", +"RC2-CBC", +"RC2-CFB", +"RC2-ECB", +"RC2-OFB", +"RC4", +"RC4-40", +"RC4-HMAC-MD5", +"RC5-CBC", +"RC5-CFB", +"RC5-ECB", +"RC5-OFB", +"RIPEMD160", +"RSA", +"RSA-MD2", +"RSA-MD4", +"RSA-MD5", +"RSA-MDC2", +"RSA-NP-MD5", +"RSA-RIPEMD160", +"RSA-SHA", +"RSA-SHA1", +"RSA-SHA1-2", +"RSA-SHA224", +"RSA-SHA256", +"RSA-SHA384", +"RSA-SHA512", +"RSAES-OAEP", +"RSASSA-PSS", +"SEED-CBC", +"SEED-CFB", +"SEED-ECB", +"SEED-OFB", +"SHA", +"SHA1", +"SHA224", +"SHA256", +"SHA384", +"SHA512", +"SMIME", +"SMIME-CAPS", +"SN", +"SNILS", +"ST", +"SXNetID", +"TLS1-PRF", +"UID", +"UNDEF", +"X25519", +"X448", +"X500", +"X500algorithms", +"X509", +"X9-57", +"X9cm", +"ZLIB", +"aRecord", +"aaControls", +"ac-auditEntity", +"ac-proxying", +"ac-targeting", +"acceptableResponses", +"account", +"ad_timestamping", +"algorithm", +"ansi-X9-62", +"anyExtendedKeyUsage", +"anyPolicy", +"archiveCutoff", +"associatedDomain", +"associatedName", +"audio", +"authorityInfoAccess", +"authorityKeyIdentifier", +"authorityRevocationList", +"basicConstraints", +"basicOCSPResponse", +"biometricInfo", +"brainpoolP160r1", +"brainpoolP160t1", +"brainpoolP192r1", +"brainpoolP192t1", +"brainpoolP224r1", +"brainpoolP224t1", +"brainpoolP256r1", +"brainpoolP256t1", +"brainpoolP320r1", +"brainpoolP320t1", +"brainpoolP384r1", +"brainpoolP384t1", +"brainpoolP512r1", +"brainpoolP512t1", +"buildingName", +"businessCategory", +"c2onb191v4", +"c2onb191v5", +"c2onb239v4", +"c2onb239v5", +"c2pnb163v1", +"c2pnb163v2", +"c2pnb163v3", +"c2pnb176v1", +"c2pnb208w1", +"c2pnb272w1", +"c2pnb304w1", +"c2pnb368w1", +"c2tnb191v1", +"c2tnb191v2", +"c2tnb191v3", +"c2tnb239v1", +"c2tnb239v2", +"c2tnb239v3", +"c2tnb359v1", +"c2tnb431r1", +"cACertificate", +"cNAMERecord", +"caIssuers", +"caRepository", +"capwapAC", +"capwapWTP", +"caseIgnoreIA5StringSyntax", +"certBag", +"certicom-arc", +"certificateIssuer", +"certificatePolicies", +"certificateRevocationList", +"challengePassword", +"characteristic-two-field", +"clearance", +"clientAuth", +"codeSigning", +"contentType", +"countersignature", +"crlBag", +"crlDistributionPoints", +"crlNumber", +"crossCertificatePair", +"cryptocom", +"cryptopro", +"ct_cert_scts", +"ct_precert_poison", +"ct_precert_scts", +"ct_precert_signer", +"dITRedirect", +"dNSDomain", +"dSAQuality", +"data", +"dcobject", +"deltaCRL", +"deltaRevocationList", +"description", +"destinationIndicator", +"dh-cofactor-kdf", +"dh-std-kdf", +"dhKeyAgreement", +"dhSinglePass-cofactorDH-sha1kdf-scheme", +"dhSinglePass-cofactorDH-sha224kdf-scheme", +"dhSinglePass-cofactorDH-sha256kdf-scheme", +"dhSinglePass-cofactorDH-sha384kdf-scheme", +"dhSinglePass-cofactorDH-sha512kdf-scheme", +"dhSinglePass-stdDH-sha1kdf-scheme", +"dhSinglePass-stdDH-sha224kdf-scheme", +"dhSinglePass-stdDH-sha256kdf-scheme", +"dhSinglePass-stdDH-sha384kdf-scheme", +"dhSinglePass-stdDH-sha512kdf-scheme", +"dhpublicnumber", +"directory", +"distinguishedName", +"dmdName", +"dnQualifier", +"document", +"documentAuthor", +"documentIdentifier", +"documentLocation", +"documentPublisher", +"documentSeries", +"documentTitle", +"documentVersion", +"domain", +"domainRelatedObject", +"dsa_with_SHA224", +"dsa_with_SHA256", +"ecdsa-with-Recommended", +"ecdsa-with-SHA1", +"ecdsa-with-SHA224", +"ecdsa-with-SHA256", +"ecdsa-with-SHA384", +"ecdsa-with-SHA512", +"ecdsa-with-Specified", +"emailAddress", +"emailProtection", +"enhancedSearchGuide", +"enterprises", +"experimental", +"extReq", +"extendedCertificateAttributes", +"extendedKeyUsage", +"extendedStatus", +"facsimileTelephoneNumber", +"favouriteDrink", +"freshestCRL", +"friendlyCountry", +"friendlyCountryName", +"friendlyName", +"generationQualifier", +"gost-mac", +"gost-mac-12", +"gost2001", +"gost2001cc", +"gost2012_256", +"gost2012_512", +"gost89", +"gost89-cbc", +"gost89-cnt", +"gost89-cnt-12", +"gost89-ctr", +"gost89-ecb", +"gost94", +"gost94cc", +"grasshopper-cbc", +"grasshopper-cfb", +"grasshopper-ctr", +"grasshopper-ecb", +"grasshopper-mac", +"grasshopper-ofb", +"hmacWithMD5", +"hmacWithSHA1", +"hmacWithSHA224", +"hmacWithSHA256", +"hmacWithSHA384", +"hmacWithSHA512", +"holdInstructionCallIssuer", +"holdInstructionCode", +"holdInstructionNone", +"holdInstructionReject", +"homePostalAddress", +"homeTelephoneNumber", +"host", +"houseIdentifier", +"iA5StringSyntax", +"id-DHBasedMac", +"id-Gost28147-89-CryptoPro-A-ParamSet", +"id-Gost28147-89-CryptoPro-B-ParamSet", +"id-Gost28147-89-CryptoPro-C-ParamSet", +"id-Gost28147-89-CryptoPro-D-ParamSet", +"id-Gost28147-89-CryptoPro-KeyMeshing", +"id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet", +"id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet", +"id-Gost28147-89-CryptoPro-RIC-1-ParamSet", +"id-Gost28147-89-None-KeyMeshing", +"id-Gost28147-89-TestParamSet", +"id-Gost28147-89-cc", +"id-GostR3410-2001-CryptoPro-A-ParamSet", +"id-GostR3410-2001-CryptoPro-B-ParamSet", +"id-GostR3410-2001-CryptoPro-C-ParamSet", +"id-GostR3410-2001-CryptoPro-XchA-ParamSet", +"id-GostR3410-2001-CryptoPro-XchB-ParamSet", +"id-GostR3410-2001-ParamSet-cc", +"id-GostR3410-2001-TestParamSet", +"id-GostR3410-2001DH", +"id-GostR3410-94-CryptoPro-A-ParamSet", +"id-GostR3410-94-CryptoPro-B-ParamSet", +"id-GostR3410-94-CryptoPro-C-ParamSet", +"id-GostR3410-94-CryptoPro-D-ParamSet", +"id-GostR3410-94-CryptoPro-XchA-ParamSet", +"id-GostR3410-94-CryptoPro-XchB-ParamSet", +"id-GostR3410-94-CryptoPro-XchC-ParamSet", +"id-GostR3410-94-TestParamSet", +"id-GostR3410-94-a", +"id-GostR3410-94-aBis", +"id-GostR3410-94-b", +"id-GostR3410-94-bBis", +"id-GostR3410-94DH", +"id-GostR3411-94-CryptoProParamSet", +"id-GostR3411-94-TestParamSet", +"id-GostR3411-94-with-GostR3410-2001", +"id-GostR3411-94-with-GostR3410-2001-cc", +"id-GostR3411-94-with-GostR3410-94", +"id-GostR3411-94-with-GostR3410-94-cc", +"id-HMACGostR3411-94", +"id-PasswordBasedMAC", +"id-aca", +"id-aca-accessIdentity", +"id-aca-authenticationInfo", +"id-aca-chargingIdentity", +"id-aca-encAttrs", +"id-aca-group", +"id-aca-role", +"id-ad", +"id-aes128-CCM", +"id-aes128-GCM", +"id-aes128-wrap", +"id-aes128-wrap-pad", +"id-aes192-CCM", +"id-aes192-GCM", +"id-aes192-wrap", +"id-aes192-wrap-pad", +"id-aes256-CCM", +"id-aes256-GCM", +"id-aes256-wrap", +"id-aes256-wrap-pad", +"id-alg", +"id-alg-PWRI-KEK", +"id-alg-des40", +"id-alg-dh-pop", +"id-alg-dh-sig-hmac-sha1", +"id-alg-noSignature", +"id-camellia128-wrap", +"id-camellia192-wrap", +"id-camellia256-wrap", +"id-cct", +"id-cct-PKIData", +"id-cct-PKIResponse", +"id-cct-crs", +"id-ce", +"id-characteristic-two-basis", +"id-cmc", +"id-cmc-addExtensions", +"id-cmc-confirmCertAcceptance", +"id-cmc-dataReturn", +"id-cmc-decryptedPOP", +"id-cmc-encryptedPOP", +"id-cmc-getCRL", +"id-cmc-getCert", +"id-cmc-identification", +"id-cmc-identityProof", +"id-cmc-lraPOPWitness", +"id-cmc-popLinkRandom", +"id-cmc-popLinkWitness", +"id-cmc-queryPending", +"id-cmc-recipientNonce", +"id-cmc-regInfo", +"id-cmc-responseInfo", +"id-cmc-revokeRequest", +"id-cmc-senderNonce", +"id-cmc-statusInfo", +"id-cmc-transactionId", +"id-ct-asciiTextWithCRLF", +"id-ct-xml", +"id-ecPublicKey", +"id-hex-multipart-message", +"id-hex-partial-message", +"id-it", +"id-it-caKeyUpdateInfo", +"id-it-caProtEncCert", +"id-it-confirmWaitTime", +"id-it-currentCRL", +"id-it-encKeyPairTypes", +"id-it-implicitConfirm", +"id-it-keyPairParamRep", +"id-it-keyPairParamReq", +"id-it-origPKIMessage", +"id-it-preferredSymmAlg", +"id-it-revPassphrase", +"id-it-signKeyPairTypes", +"id-it-subscriptionRequest", +"id-it-subscriptionResponse", +"id-it-suppLangTags", +"id-it-unsupportedOIDs", +"id-kp", +"id-mod-attribute-cert", +"id-mod-cmc", +"id-mod-cmp", +"id-mod-cmp2000", +"id-mod-crmf", +"id-mod-dvcs", +"id-mod-kea-profile-88", +"id-mod-kea-profile-93", +"id-mod-ocsp", +"id-mod-qualified-cert-88", +"id-mod-qualified-cert-93", +"id-mod-timestamp-protocol", +"id-on", +"id-on-permanentIdentifier", +"id-on-personalData", +"id-pda", +"id-pda-countryOfCitizenship", +"id-pda-countryOfResidence", +"id-pda-dateOfBirth", +"id-pda-gender", +"id-pda-placeOfBirth", +"id-pe", +"id-pkinit", +"id-pkip", +"id-pkix-mod", +"id-pkix1-explicit-88", +"id-pkix1-explicit-93", +"id-pkix1-implicit-88", +"id-pkix1-implicit-93", +"id-ppl", +"id-ppl-anyLanguage", +"id-ppl-independent", +"id-ppl-inheritAll", +"id-qcs", +"id-qcs-pkixQCSyntax-v1", +"id-qt", +"id-qt-cps", +"id-qt-unotice", +"id-regCtrl", +"id-regCtrl-authenticator", +"id-regCtrl-oldCertID", +"id-regCtrl-pkiArchiveOptions", +"id-regCtrl-pkiPublicationInfo", +"id-regCtrl-protocolEncrKey", +"id-regCtrl-regToken", +"id-regInfo", +"id-regInfo-certReq", +"id-regInfo-utf8Pairs", +"id-scrypt", +"id-set", +"id-smime-aa", +"id-smime-aa-contentHint", +"id-smime-aa-contentIdentifier", +"id-smime-aa-contentReference", +"id-smime-aa-dvcs-dvc", +"id-smime-aa-encapContentType", +"id-smime-aa-encrypKeyPref", +"id-smime-aa-equivalentLabels", +"id-smime-aa-ets-CertificateRefs", +"id-smime-aa-ets-RevocationRefs", +"id-smime-aa-ets-archiveTimeStamp", +"id-smime-aa-ets-certCRLTimestamp", +"id-smime-aa-ets-certValues", +"id-smime-aa-ets-commitmentType", +"id-smime-aa-ets-contentTimestamp", +"id-smime-aa-ets-escTimeStamp", +"id-smime-aa-ets-otherSigCert", +"id-smime-aa-ets-revocationValues", +"id-smime-aa-ets-sigPolicyId", +"id-smime-aa-ets-signerAttr", +"id-smime-aa-ets-signerLocation", +"id-smime-aa-macValue", +"id-smime-aa-mlExpandHistory", +"id-smime-aa-msgSigDigest", +"id-smime-aa-receiptRequest", +"id-smime-aa-securityLabel", +"id-smime-aa-signatureType", +"id-smime-aa-signingCertificate", +"id-smime-aa-smimeEncryptCerts", +"id-smime-aa-timeStampToken", +"id-smime-alg", +"id-smime-alg-3DESwrap", +"id-smime-alg-CMS3DESwrap", +"id-smime-alg-CMSRC2wrap", +"id-smime-alg-ESDH", +"id-smime-alg-ESDHwith3DES", +"id-smime-alg-ESDHwithRC2", +"id-smime-alg-RC2wrap", +"id-smime-cd", +"id-smime-cd-ldap", +"id-smime-ct", +"id-smime-ct-DVCSRequestData", +"id-smime-ct-DVCSResponseData", +"id-smime-ct-TDTInfo", +"id-smime-ct-TSTInfo", +"id-smime-ct-authData", +"id-smime-ct-authEnvelopedData", +"id-smime-ct-compressedData", +"id-smime-ct-contentCollection", +"id-smime-ct-contentInfo", +"id-smime-ct-publishCert", +"id-smime-ct-receipt", +"id-smime-cti", +"id-smime-cti-ets-proofOfApproval", +"id-smime-cti-ets-proofOfCreation", +"id-smime-cti-ets-proofOfDelivery", +"id-smime-cti-ets-proofOfOrigin", +"id-smime-cti-ets-proofOfReceipt", +"id-smime-cti-ets-proofOfSender", +"id-smime-mod", +"id-smime-mod-cms", +"id-smime-mod-ess", +"id-smime-mod-ets-eSigPolicy-88", +"id-smime-mod-ets-eSigPolicy-97", +"id-smime-mod-ets-eSignature-88", +"id-smime-mod-ets-eSignature-97", +"id-smime-mod-msg-v3", +"id-smime-mod-oid", +"id-smime-spq", +"id-smime-spq-ets-sqt-unotice", +"id-smime-spq-ets-sqt-uri", +"id-tc26", +"id-tc26-agreement", +"id-tc26-agreement-gost-3410-2012-256", +"id-tc26-agreement-gost-3410-2012-512", +"id-tc26-algorithms", +"id-tc26-cipher", +"id-tc26-cipher-constants", +"id-tc26-constants", +"id-tc26-digest", +"id-tc26-digest-constants", +"id-tc26-gost-28147-constants", +"id-tc26-gost-28147-param-Z", +"id-tc26-gost-3410-2012-512-constants", +"id-tc26-gost-3410-2012-512-paramSetA", +"id-tc26-gost-3410-2012-512-paramSetB", +"id-tc26-gost-3410-2012-512-paramSetTest", +"id-tc26-hmac-gost-3411-2012-256", +"id-tc26-hmac-gost-3411-2012-512", +"id-tc26-mac", +"id-tc26-sign", +"id-tc26-sign-constants", +"id-tc26-signwithdigest", +"id-tc26-signwithdigest-gost3410-2012-256", +"id-tc26-signwithdigest-gost3410-2012-512", +"identified-organization", +"info", +"inhibitAnyPolicy", +"initials", +"international-organizations", +"internationaliSDNNumber", +"invalidityDate", +"ipsecEndSystem", +"ipsecIKE", +"ipsecTunnel", +"ipsecUser", +"issuerAltName", +"issuerSignTool", +"issuingDistributionPoint", +"janetMailbox", +"jurisdictionC", +"jurisdictionL", +"jurisdictionST", +"keyBag", +"keyUsage", +"lastModifiedBy", +"lastModifiedTime", +"localKeyID", +"mXRecord", +"mail", +"mailPreferenceOption", +"manager", +"md_gost12_256", +"md_gost12_512", +"md_gost94", +"member", +"member-body", +"messageDigest", +"mgmt", +"mime-mhs", +"mime-mhs-bodies", +"mime-mhs-headings", +"mobileTelephoneNumber", +"msCTLSign", +"msCodeCom", +"msCodeInd", +"msEFS", +"msExtReq", +"msSGC", +"msSmartcardLogin", +"msUPN", +"nSRecord", +"name", +"nameConstraints", +"noCheck", +"noRevAvail", +"nsBaseUrl", +"nsCaPolicyUrl", +"nsCaRevocationUrl", +"nsCertExt", +"nsCertSequence", +"nsCertType", +"nsComment", +"nsDataType", +"nsRenewalUrl", +"nsRevocationUrl", +"nsSGC", +"nsSslServerName", +"onBasis", +"organizationalStatus", +"otherMailbox", +"owner", +"pagerTelephoneNumber", +"path", +"pbeWithMD5AndCast5CBC", +"personalSignature", +"personalTitle", +"photo", +"physicalDeliveryOfficeName", +"pilot", +"pilotAttributeSyntax", +"pilotAttributeType", +"pilotAttributeType27", +"pilotDSA", +"pilotGroups", +"pilotObject", +"pilotObjectClass", +"pilotOrganization", +"pilotPerson", +"pkInitClientAuth", +"pkInitKDC", +"pkcs", +"pkcs1", +"pkcs3", +"pkcs5", +"pkcs7", +"pkcs7-data", +"pkcs7-digestData", +"pkcs7-encryptedData", +"pkcs7-envelopedData", +"pkcs7-signedAndEnvelopedData", +"pkcs7-signedData", +"pkcs8ShroudedKeyBag", +"pkcs9", +"policyConstraints", +"policyMappings", +"postOfficeBox", +"postalAddress", +"postalCode", +"ppBasis", +"preferredDeliveryMethod", +"presentationAddress", +"prf-gostr3411-94", +"prime-field", +"prime192v1", +"prime192v2", +"prime192v3", +"prime239v1", +"prime239v2", +"prime239v3", +"prime256v1", +"private", +"privateKeyUsagePeriod", +"protocolInformation", +"proxyCertInfo", +"pseudonym", +"pss", +"qcStatements", +"qualityLabelledData", +"rFC822localPart", +"registeredAddress", +"role", +"roleOccupant", +"room", +"roomNumber", +"rsaEncryption", +"rsaOAEPEncryptionSET", +"rsaSignature", +"rsadsi", +"sOARecord", +"safeContentsBag", +"sbgp-autonomousSysNum", +"sbgp-ipAddrBlock", +"sbgp-routerIdentifier", +"sdsiCertificate", +"searchGuide", +"secp112r1", +"secp112r2", +"secp128r1", +"secp128r2", +"secp160k1", +"secp160r1", +"secp160r2", +"secp192k1", +"secp224k1", +"secp224r1", +"secp256k1", +"secp384r1", +"secp521r1", +"secretBag", +"secretary", +"sect113r1", +"sect113r2", +"sect131r1", +"sect131r2", +"sect163k1", +"sect163r1", +"sect163r2", +"sect193r1", +"sect193r2", +"sect233k1", +"sect233r1", +"sect239k1", +"sect283k1", +"sect283r1", +"sect409k1", +"sect409r1", +"sect571k1", +"sect571r1", +"secureShellClient", +"secureShellServer", +"security", +"seeAlso", +"selected-attribute-types", +"sendOwner", +"sendProxiedOwner", +"sendProxiedRouter", +"sendRouter", +"serialNumber", +"serverAuth", +"serviceLocator", +"set-addPolicy", +"set-attr", +"set-brand", +"set-brand-AmericanExpress", +"set-brand-Diners", +"set-brand-IATA-ATA", +"set-brand-JCB", +"set-brand-MasterCard", +"set-brand-Novus", +"set-brand-Visa", +"set-certExt", +"set-ctype", +"set-msgExt", +"set-policy", +"set-policy-root", +"set-rootKeyThumb", +"setAttr-Cert", +"setAttr-GenCryptgrm", +"setAttr-IssCap", +"setAttr-IssCap-CVM", +"setAttr-IssCap-Sig", +"setAttr-IssCap-T2", +"setAttr-PGWYcap", +"setAttr-SecDevSig", +"setAttr-T2Enc", +"setAttr-T2cleartxt", +"setAttr-TokICCsig", +"setAttr-Token-B0Prime", +"setAttr-Token-EMV", +"setAttr-TokenType", +"setCext-IssuerCapabilities", +"setCext-PGWYcapabilities", +"setCext-TokenIdentifier", +"setCext-TokenType", +"setCext-Track2Data", +"setCext-cCertRequired", +"setCext-certType", +"setCext-hashedRoot", +"setCext-merchData", +"setCext-setExt", +"setCext-setQualf", +"setCext-tunneling", +"setct-AcqCardCodeMsg", +"setct-AcqCardCodeMsgTBE", +"setct-AuthReqTBE", +"setct-AuthReqTBS", +"setct-AuthResBaggage", +"setct-AuthResTBE", +"setct-AuthResTBEX", +"setct-AuthResTBS", +"setct-AuthResTBSX", +"setct-AuthRevReqBaggage", +"setct-AuthRevReqTBE", +"setct-AuthRevReqTBS", +"setct-AuthRevResBaggage", +"setct-AuthRevResData", +"setct-AuthRevResTBE", +"setct-AuthRevResTBEB", +"setct-AuthRevResTBS", +"setct-AuthTokenTBE", +"setct-AuthTokenTBS", +"setct-BCIDistributionTBS", +"setct-BatchAdminReqData", +"setct-BatchAdminReqTBE", +"setct-BatchAdminResData", +"setct-BatchAdminResTBE", +"setct-CRLNotificationResTBS", +"setct-CRLNotificationTBS", +"setct-CapReqTBE", +"setct-CapReqTBEX", +"setct-CapReqTBS", +"setct-CapReqTBSX", +"setct-CapResData", +"setct-CapResTBE", +"setct-CapRevReqTBE", +"setct-CapRevReqTBEX", +"setct-CapRevReqTBS", +"setct-CapRevReqTBSX", +"setct-CapRevResData", +"setct-CapRevResTBE", +"setct-CapTokenData", +"setct-CapTokenSeq", +"setct-CapTokenTBE", +"setct-CapTokenTBEX", +"setct-CapTokenTBS", +"setct-CardCInitResTBS", +"setct-CertInqReqTBS", +"setct-CertReqData", +"setct-CertReqTBE", +"setct-CertReqTBEX", +"setct-CertReqTBS", +"setct-CertResData", +"setct-CertResTBE", +"setct-CredReqTBE", +"setct-CredReqTBEX", +"setct-CredReqTBS", +"setct-CredReqTBSX", +"setct-CredResData", +"setct-CredResTBE", +"setct-CredRevReqTBE", +"setct-CredRevReqTBEX", +"setct-CredRevReqTBS", +"setct-CredRevReqTBSX", +"setct-CredRevResData", +"setct-CredRevResTBE", +"setct-ErrorTBS", +"setct-HODInput", +"setct-MeAqCInitResTBS", +"setct-OIData", +"setct-PANData", +"setct-PANOnly", +"setct-PANToken", +"setct-PCertReqData", +"setct-PCertResTBS", +"setct-PI", +"setct-PI-TBS", +"setct-PIData", +"setct-PIDataUnsigned", +"setct-PIDualSignedTBE", +"setct-PIUnsignedTBE", +"setct-PInitResData", +"setct-PResData", +"setct-RegFormReqTBE", +"setct-RegFormResTBS", +"setext-cv", +"setext-genCrypt", +"setext-miAuth", +"setext-pinAny", +"setext-pinSecure", +"setext-track2", +"signingTime", +"simpleSecurityObject", +"singleLevelQuality", +"snmpv2", +"street", +"subjectAltName", +"subjectDirectoryAttributes", +"subjectInfoAccess", +"subjectKeyIdentifier", +"subjectSignTool", +"subtreeMaximumQuality", +"subtreeMinimumQuality", +"supportedAlgorithms", +"supportedApplicationContext", +"targetInformation", +"telephoneNumber", +"teletexTerminalIdentifier", +"telexNumber", +"textEncodedORAddress", +"textNotice", +"timeStamping", +"title", +"tlsfeature", +"tpBasis", +"trustRoot", +"ucl", +"uid", +"uniqueMember", +"unstructuredAddress", +"unstructuredName", +"userCertificate", +"userClass", +"userPassword", +"valid", +"wap", +"wap-wsg", +"wap-wsg-idm-ecid-wtls1", +"wap-wsg-idm-ecid-wtls10", +"wap-wsg-idm-ecid-wtls11", +"wap-wsg-idm-ecid-wtls12", +"wap-wsg-idm-ecid-wtls3", +"wap-wsg-idm-ecid-wtls4", +"wap-wsg-idm-ecid-wtls5", +"wap-wsg-idm-ecid-wtls6", +"wap-wsg-idm-ecid-wtls7", +"wap-wsg-idm-ecid-wtls8", +"wap-wsg-idm-ecid-wtls9", +"whirlpool", +"x121Address", +"x500UniqueIdentifier", +"x509Certificate", +"x509Crl", +}; diff --git a/src/openvpn/console_builtin.c b/src/openvpn/console_builtin.c index 3b97aad9eb2..719989a9fd1 100644 --- a/src/openvpn/console_builtin.c +++ b/src/openvpn/console_builtin.c @@ -157,7 +157,11 @@ static FILE * open_tty(const bool write) { FILE *ret; + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + ret = fuzz_fopen("/dev/tty", write ? "w" : "r"); + #else ret = fopen("/dev/tty", write ? "w" : "r"); + #endif if (!ret) { ret = write ? stderr : stdin; @@ -176,7 +180,11 @@ close_tty(FILE *fp) { if (fp != stderr && fp != stdin) { + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + fuzz_fclose(fp); + #else fclose(fp); + #endif } } @@ -212,7 +220,11 @@ get_console_input(const char *prompt, const bool echo, char *input, const int ca * (in which case neither stdin or stderr are connected to a tty and * /dev/tty can not be open()ed anymore) */ + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (!fuzz_isatty(0) && !fuzz_isatty(2) ) + #else if (!isatty(0) && !isatty(2) ) + #endif { int fd = open( "/dev/tty", O_RDWR ); if (fd < 0) @@ -239,7 +251,11 @@ get_console_input(const char *prompt, const bool echo, char *input, const int ca restore_tty = (tcsetattr(fileno(fp), TCSAFLUSH, &tty_tmp) == 0); } + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (fuzz_fgets(input, capacity, fp) != NULL) + #else if (fgets(input, capacity, fp) != NULL) + #endif { chomp(input); ret = true; diff --git a/src/openvpn/console_systemd.c b/src/openvpn/console_systemd.c index c7cf1adad38..b59dbea41de 100644 --- a/src/openvpn/console_systemd.c +++ b/src/openvpn/console_systemd.c @@ -78,7 +78,11 @@ get_console_input_systemd(const char *prompt, const bool echo, char *input, cons return false; } memset(input, 0, capacity); + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (fuzz_read(std_out, input, capacity-1) != 0) + #else if (read(std_out, input, capacity-1) != 0) + #endif { chomp(input); ret = true; diff --git a/src/openvpn/error.c b/src/openvpn/error.c index c8c77358ea7..779b2387c93 100644 --- a/src/openvpn/error.c +++ b/src/openvpn/error.c @@ -198,7 +198,11 @@ msg_fp(const unsigned int flags) FILE *fp = msgfp; if (!fp) { + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + fp = stdout; + #else fp = (flags & (M_FATAL|M_USAGE_SMALL)) ? default_err : default_out; + #endif } if (!fp) { diff --git a/src/openvpn/fake_fuzz_header.h b/src/openvpn/fake_fuzz_header.h new file mode 100644 index 00000000000..dcaceba0f23 --- /dev/null +++ b/src/openvpn/fake_fuzz_header.h @@ -0,0 +1,2 @@ +ssize_t fuzz_get_random_data(void *buf, size_t len) { return 0; } +int fuzz_success; diff --git a/src/openvpn/fuzz.h b/src/openvpn/fuzz.h new file mode 100644 index 00000000000..10e33c3ee28 --- /dev/null +++ b/src/openvpn/fuzz.h @@ -0,0 +1,47 @@ +/* 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 + +// Returns a NULL-terminated C string that should be freed by the caller. +char *get_modifiable_string(FuzzedDataProvider &provider) { + std::string s1 = provider.ConsumeRandomLengthString(); + char *tmp = (char *)malloc(s1.size() + 1); + memcpy(tmp, s1.c_str(), s1.size()); + tmp[s1.size()] = '\0'; + return tmp; +} + +FuzzedDataProvider *prov = NULL; + + +extern "C" ssize_t fuzz_get_random_data(void *buf, size_t len) { + size_t ret_val; + char *cbuf = (char*)buf; + + if (prov->remaining_bytes() == 0) { + return -1; + } + + double prob = prov->ConsumeProbability(); + if (prob < 0.05) { + return 0; + } + + if (len == 1) { + ret_val = prov->ConsumeData(buf, 1); + return ret_val; + } + ret_val = prov->ConsumeData(buf, len); + return ret_val; +} + diff --git a/src/openvpn/fuzz_header.h b/src/openvpn/fuzz_header.h new file mode 100644 index 00000000000..e86b70c62b8 --- /dev/null +++ b/src/openvpn/fuzz_header.h @@ -0,0 +1,79 @@ +/* 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. +*/ + +#ifndef FUZZ_H +#define FUZZ_H + +#include +#include + +// Forward declared because we want to use FuzzedDataProvider, +// which requires CPP. +extern ssize_t fuzz_get_random_data(void *buf, size_t len); + +ssize_t fuzz_recv(int sockfd, void *buf, size_t len, int flags){ + return fuzz_get_random_data(buf, len); +} + +ssize_t fuzz_read(int sockfd, void *buf, size_t len){ + return fuzz_get_random_data(buf, len); +} + +ssize_t fuzz_write(int fd, const void *buf, size_t count) { + return count; +} + +int fuzz_isatty(int fd) { + return 1; +} + +char *fuzz_fgets(char *s, int size, FILE *stream) { + ssize_t v = fuzz_get_random_data(s, size-1); + // We use fgets to get trusted input. As such, assume we have + // an ascii printable char at the beginning. + printf("Calling into fgets\n"); + if (s[0] <= 0x21 || s[0] >= 0x7f) { + s[0] = 'A'; + } + s[size-1] = '\0'; + return s; +} + +int fuzz_select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout) { + char val; + ssize_t c = fuzz_get_random_data(&val, 1); + return c; +} + +ssize_t fuzz_send(int sockfd, const void *buf, size_t len, int flags) { + return len; +} + +FILE *fp_p = NULL; +FILE *fuzz_fopen(const char *pathname, const char *mode) { + if (mode == NULL) return fp_p; + return fp_p; +} + +int fuzz_fclose(FILE *stream) { + if (stream == NULL) return 1; + return 2; +} + +size_t fuzz_sendto(int sockfd, void *buf, size_t len, int flags, struct sockaddr *dest_addr, socklen_t addrlen) { + if (buf == NULL) { + return len; + } + return len; +} + +#endif diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c index 50f7f97581b..0622b140a5d 100644 --- a/src/openvpn/misc.c +++ b/src/openvpn/misc.c @@ -223,7 +223,11 @@ get_user_pass_cr(struct user_pass *up, if ((flags & GET_USER_PASS_PASSWORD_ONLY) == 0) { /* Read username first */ + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (fuzz_fgets(up->username, USER_PASS_LEN, fp) == NULL) + #else if (fgets(up->username, USER_PASS_LEN, fp) == NULL) + #endif { msg(M_FATAL, "Error reading username from %s authfile: %s", prefix, @@ -232,7 +236,11 @@ get_user_pass_cr(struct user_pass *up, } chomp(up->username); + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (fuzz_fgets(password_buf, USER_PASS_LEN, fp) != NULL) + #else if (fgets(password_buf, USER_PASS_LEN, fp) != NULL) + #endif { chomp(password_buf); } @@ -775,7 +783,9 @@ output_peer_info_env(struct env_set *es, const char *peer_info) } else { + #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION msg(M_WARN, "validation failed on peer_info line received from client"); + #endif } } } diff --git a/src/openvpn/mroute.c b/src/openvpn/mroute.c index 64404cdb6d4..45fc5516a89 100644 --- a/src/openvpn/mroute.c +++ b/src/openvpn/mroute.c @@ -206,9 +206,11 @@ mroute_extract_addr_ip(struct mroute_addr *src, struct mroute_addr *dest, } break; + #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION default: msg(M_WARN, "IP packet with unknown IP version=%d seen", OPENVPN_IPH_GET_VER(*BPTR(buf))); + #endif } } return ret; diff --git a/src/openvpn/openvpn.c b/src/openvpn/openvpn.c index 15e21452b6f..cd2cbc3e272 100644 --- a/src/openvpn/openvpn.c +++ b/src/openvpn/openvpn.c @@ -392,3 +392,7 @@ main(int argc, char *argv[]) return openvpn_main(argc, argv); } #endif /* ifdef _WIN32 */ + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#include "fake_fuzz_header.h" +#endif diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c index 1872bd13fb3..5340eeb80a9 100644 --- a/src/openvpn/packet_id.c +++ b/src/openvpn/packet_id.c @@ -494,7 +494,11 @@ packet_id_persist_save(struct packet_id_persist *p) seek_ret = lseek(p->fd, (off_t)0, SEEK_SET); if (seek_ret == (off_t)0) { + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + n = fuzz_write(p->fd, &image, sizeof(image)); + #else n = write(p->fd, &image, sizeof(image)); + #endif if (n == sizeof(image)) { p->time_last_written = p->time; diff --git a/src/openvpn/proxy.c b/src/openvpn/proxy.c index ed7201616a4..80a04e44eee 100644 --- a/src/openvpn/proxy.c +++ b/src/openvpn/proxy.c @@ -40,6 +40,9 @@ #include "httpdigest.h" #include "ntlm.h" #include "memdbg.h" +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#include "fuzz_header.h" +#endif #include "forward.h" #define UP_TYPE_PROXY "HTTP Proxy" @@ -97,7 +100,11 @@ recv_line(socket_descriptor_t sd, tv.tv_sec = timeout_sec; tv.tv_usec = 0; + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + status = fuzz_select(sd + 1, &reads, NULL, NULL, &tv); + #else status = select(sd + 1, &reads, NULL, NULL, &tv); + #endif get_signal(signal_received); if (*signal_received) @@ -126,7 +133,11 @@ recv_line(socket_descriptor_t sd, } /* read single char */ + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + size = fuzz_recv(sd, &c, 1, MSG_NOSIGNAL); + #else size = recv(sd, &c, 1, MSG_NOSIGNAL); + #endif /* error? */ if (size != 1) @@ -196,7 +207,11 @@ static bool send_line(socket_descriptor_t sd, const char *buf) { + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + const ssize_t size = fuzz_send(sd, buf, strlen(buf), MSG_NOSIGNAL); + #else const ssize_t size = send(sd, buf, strlen(buf), MSG_NOSIGNAL); + #endif if (size != (ssize_t) strlen(buf)) { msg(D_LINK_ERRORS | M_ERRNO, "send_line: TCP port write failed on send()"); @@ -772,7 +787,12 @@ establish_http_proxy_passthru(struct http_proxy_info *p, buf2[128] = 0; /* we only need the beginning - ensure it's null terminated. */ /* check for "Proxy-Authenticate: NTLM TlRM..." */ + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // this condition is hard to pass for fuzzer as the loop consumes a lot of input + if (true) + #else if (nparms == 1) + #endif { /* parse buf2 */ msg(D_PROXY, "auth string: '%s'", buf2); diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index 4a982561914..72ece81668d 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -3234,7 +3234,11 @@ link_socket_read_tcp(struct link_socket *sock, #else struct buffer frag; stream_buf_get_next(&sock->stream_buf, &frag); + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + len = fuzz_recv(sock->sd, BPTR(&frag), BLEN(&frag), MSG_NOSIGNAL); + #else len = recv(sock->sd, BPTR(&frag), BLEN(&frag), MSG_NOSIGNAL); + #endif #endif if (!len) diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h index 462afa31bb4..a5a33249115 100644 --- a/src/openvpn/socket.h +++ b/src/openvpn/socket.h @@ -34,6 +34,11 @@ #include "proxy.h" #include "socks.h" #include "misc.h" + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +extern size_t fuzz_sendto(int sockfd, void *buf, size_t len, int flags, struct sockaddr *dest_addr, socklen_t addrlen); +#endif + #include "tun.h" /* @@ -1157,7 +1162,11 @@ link_socket_write_udp_posix(struct link_socket *sock, } else #endif + #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + return fuzz_sendto(sock->sd, BPTR(buf), BLEN(buf), 0, + #else return sendto(sock->sd, BPTR(buf), BLEN(buf), 0, + #endif (struct sockaddr *) &to->dest.addr.sa, (socklen_t) af_addr_size(to->dest.addr.sa.sa_family)); }