diff options
Diffstat (limited to '')
-rw-r--r-- | tests/fuzz/LUKS2.proto | 6 | ||||
-rw-r--r-- | tests/fuzz/LUKS2_plain_JSON.proto | 4 | ||||
-rw-r--r-- | tests/fuzz/crypt2_load_fuzz.cc | 111 | ||||
-rw-r--r-- | tests/fuzz/crypt2_load_proto_fuzz.cc | 4 | ||||
-rw-r--r-- | tests/fuzz/crypt2_load_proto_plain_json_fuzz.cc | 4 | ||||
-rw-r--r-- | tests/fuzz/meson.build | 127 | ||||
-rwxr-xr-x | tests/fuzz/oss-fuzz-build.sh | 20 | ||||
-rw-r--r-- | tests/fuzz/plain_json_proto_to_luks2.cc | 4 | ||||
-rw-r--r-- | tests/fuzz/plain_json_proto_to_luks2_converter.cc | 19 | ||||
-rw-r--r-- | tests/fuzz/plain_json_proto_to_luks2_converter.h | 4 | ||||
-rw-r--r-- | tests/fuzz/proto_to_luks2.cc | 4 | ||||
-rw-r--r-- | tests/fuzz/proto_to_luks2_converter.cc | 4 | ||||
-rw-r--r-- | tests/fuzz/proto_to_luks2_converter.h | 4 |
13 files changed, 223 insertions, 92 deletions
diff --git a/tests/fuzz/LUKS2.proto b/tests/fuzz/LUKS2.proto index 3a0f287..f54ed6b 100644 --- a/tests/fuzz/LUKS2.proto +++ b/tests/fuzz/LUKS2.proto @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 custom mutator * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -229,7 +229,7 @@ enum keyslot_af_type { KEYSLOT_AF_TYPE_LUKS1 = 1; } -// The af (anti-forensic splitter) object contains this madatory field: +// The af (anti-forensic splitter) object contains this mandatory field: // - type [string] the anti-forensic function type. // AF type luks1 (compatible with LUKS1 [1]) contains these additional fields: // - stripes [integer] the number of stripes, for historical reasons only the 4000 value is supported. diff --git a/tests/fuzz/LUKS2_plain_JSON.proto b/tests/fuzz/LUKS2_plain_JSON.proto index 59096b7..da8ea00 100644 --- a/tests/fuzz/LUKS2_plain_JSON.proto +++ b/tests/fuzz/LUKS2_plain_JSON.proto @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 custom mutator * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/tests/fuzz/crypt2_load_fuzz.cc b/tests/fuzz/crypt2_load_fuzz.cc index 1251d72..2195b40 100644 --- a/tests/fuzz/crypt2_load_fuzz.cc +++ b/tests/fuzz/crypt2_load_fuzz.cc @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 fuzz target * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,91 +22,88 @@ extern "C" { #define FILESIZE (16777216) #include "src/cryptsetup.h" -#include <err.h> #include "luks2/luks2.h" #include "crypto_backend/crypto_backend.h" #include "FuzzerInterface.h" -static int calculate_checksum(const uint8_t* data, size_t size) { - struct crypt_hash *hd = NULL; - struct luks2_hdr_disk *hdr = NULL; - int hash_size; - uint64_t hdr_size1, hdr_size2; - int r = 0; - - /* primary header */ - if (sizeof(struct luks2_hdr_disk) > size) - return 0; - hdr = CONST_CAST(struct luks2_hdr_disk *) data; - - hdr_size1 = be64_to_cpu(hdr->hdr_size); - if (hdr_size1 > size) - return 0; - memset(&hdr->csum, 0, LUKS2_CHECKSUM_L); - if ((r = crypt_hash_init(&hd, "sha256"))) - goto out; - if ((r = crypt_hash_write(hd, CONST_CAST(char*) data, hdr_size1))) - goto out; - hash_size = crypt_hash_size("sha256"); - if (hash_size <= 0) { - r = 1; - goto out; - } - if ((r = crypt_hash_final(hd, (char*)&hdr->csum, (size_t)hash_size))) - goto out; - crypt_hash_destroy(hd); +#define CHKSUM_ALG "sha256" +#define CHKSUM_SIZE 32 - /* secondary header */ - if (hdr_size1 < sizeof(struct luks2_hdr_disk)) - hdr_size1 = sizeof(struct luks2_hdr_disk); +static bool fix_checksum_hdr(struct luks2_hdr_disk *hdr, const char *data, size_t len) +{ + char *csum = (char *)&hdr->csum; + struct crypt_hash *hd = NULL; + bool r = false; - if (hdr_size1 + sizeof(struct luks2_hdr_disk) > size) - return 0; - hdr = CONST_CAST(struct luks2_hdr_disk *) (data + hdr_size1); + if (crypt_hash_init(&hd, CHKSUM_ALG)) + return false; - hdr_size2 = be64_to_cpu(hdr->hdr_size); - if (hdr_size2 > size || (hdr_size1 + hdr_size2) > size) - return 0; + memset(csum, 0, LUKS2_CHECKSUM_L); - memset(&hdr->csum, 0, LUKS2_CHECKSUM_L); - if ((r = crypt_hash_init(&hd, "sha256"))) - goto out; - if ((r = crypt_hash_write(hd, (char*) hdr, hdr_size2))) - goto out; - if ((r = crypt_hash_final(hd, (char*)&hdr->csum, (size_t)hash_size))) - goto out; + if (!crypt_hash_write(hd, data, len) && + !crypt_hash_final(hd, csum, CHKSUM_SIZE)) + r = true; -out: - if (hd) - crypt_hash_destroy(hd); + crypt_hash_destroy(hd); return r; } -int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - int fd; +static bool calculate_checksum(const char *data, size_t size, struct luks2_hdr_disk *hdr_rw) +{ + uint64_t hdr_size; + + /* Primary header cannot fit in data */ + if (sizeof(*hdr_rw) > size) + return false; + + hdr_size = be64_to_cpu(((struct luks2_hdr_disk *)data)->hdr_size); + if (hdr_size > size || hdr_size <= sizeof(*hdr_rw)) + return false; + + /* Calculate checksum for primary header */ + memcpy(hdr_rw, data, sizeof(*hdr_rw)); + return fix_checksum_hdr(hdr_rw, data, (size_t)hdr_size); +} + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + int fd, r = EXIT_FAILURE; struct crypt_device *cd = NULL; char name[] = "/tmp/test-script-fuzz.XXXXXX"; + struct luks2_hdr_disk hdr_rw; + size_t modified_data_size; - if (calculate_checksum(data, size)) - return 0; + /* if csum calculation fails, keep fuzzer running on original input */ + if (size >= sizeof(hdr_rw) && calculate_checksum((const char *)data, size, &hdr_rw)) + modified_data_size = sizeof(hdr_rw); + else + modified_data_size = 0; + /* create file with LUKS header for libcryptsetup */ fd = mkostemp(name, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC); if (fd == -1) - err(EXIT_FAILURE, "mkostemp() failed"); + return r; /* enlarge header */ if (ftruncate(fd, FILESIZE) == -1) goto out; - if (write_buffer(fd, data, size) != (ssize_t)size) + if (modified_data_size && + write_buffer(fd, &hdr_rw, modified_data_size) != (ssize_t)modified_data_size) + goto out; + + if (write_buffer(fd, data + modified_data_size, size - modified_data_size) != (ssize_t)size) goto out; + /* Actual fuzzing */ if (crypt_init(&cd, name) == 0) (void)crypt_load(cd, CRYPT_LUKS2, NULL); crypt_free(cd); + r = 0; out: close(fd); unlink(name); - return 0; + + return r; } } diff --git a/tests/fuzz/crypt2_load_proto_fuzz.cc b/tests/fuzz/crypt2_load_proto_fuzz.cc index 498c006..aaabfe8 100644 --- a/tests/fuzz/crypt2_load_proto_fuzz.cc +++ b/tests/fuzz/crypt2_load_proto_fuzz.cc @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 custom mutator fuzz target * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/tests/fuzz/crypt2_load_proto_plain_json_fuzz.cc b/tests/fuzz/crypt2_load_proto_plain_json_fuzz.cc index f3565ab..227c49a 100644 --- a/tests/fuzz/crypt2_load_proto_plain_json_fuzz.cc +++ b/tests/fuzz/crypt2_load_proto_plain_json_fuzz.cc @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 custom mutator fuzz target * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build new file mode 100644 index 0000000..95ea382 --- /dev/null +++ b/tests/fuzz/meson.build @@ -0,0 +1,127 @@ +if get_option('fuzz-targets') + crypt2_load_fuzz = executable('crypt2_load_fuzz', + [ + 'crypt2_load_fuzz.cc', + ], + dependencies: [ + devmapper, + fuzzing_engine, + ], + link_with: [ + libcryptsetup, + libcrypto_backend, + libutils_io, + ], + link_args: [ + link_args, + ], + include_directories: includes_tools) + + crypt2_load_ondisk_fuzz = executable('crypt2_load_ondisk_fuzz', + [ + 'crypt2_load_ondisk_fuzz.cc', + ], + dependencies: [ + devmapper, + fuzzing_engine, + ], + link_with: [ + libcryptsetup, + libcrypto_backend, + libutils_io, + ], + link_args: [ + link_args, + ], + include_directories: includes_tools) + + luks2_generated = protoc_generator.process('LUKS2.proto') + crypt2_load_proto_fuzz = executable('crypt2_load_proto_fuzz', + [ + 'crypt2_load_proto_fuzz.cc', + 'proto_to_luks2_converter.cc', + luks2_generated, + ], + dependencies: [ + devmapper, + protobuf, + libprotobuf_mutator, + fuzzing_engine, + ], + link_with: [ + libcryptsetup, + libcrypto_backend, + libutils_io, + ], + link_args: [ + link_args, + ], + include_directories: includes_tools) + + luks2_plain_json_generated = protoc_generator.process('LUKS2_plain_JSON.proto') + crypt2_load_proto_plain_fuzz = executable('crypt2_load_proto_plain_fuzz', + [ + 'crypt2_load_proto_plain_json_fuzz.cc', + 'json_proto_converter.cc', + 'plain_json_proto_to_luks2_converter.cc', + luks2_plain_json_generated, + ], + dependencies: [ + devmapper, + protobuf, + libprotobuf_mutator, + fuzzing_engine, + ], + link_with: [ + libcryptsetup, + libcrypto_backend, + libutils_io, + ], + link_args: [ + link_args, + ], + include_directories: includes_tools) + + proto_to_luks2 = executable('proto_to_luks2', + [ + 'proto_to_luks2.cc', + 'proto_to_luks2_converter.cc', + luks2_generated, + ], + dependencies: [ + devmapper, + protobuf, + libprotobuf_mutator, + ], + link_with: [ + libcryptsetup, + libcrypto_backend, + libutils_io, + ], + link_args: [ + link_args, + ], + include_directories: includes_tools) + + plain_json_proto_to_luks2 = executable('plain_json_proto_to_luks2', + [ + 'plain_json_proto_to_luks2.cc', + 'plain_json_proto_to_luks2_converter.cc', + 'json_proto_converter.cc', + luks2_plain_json_generated, + ], + dependencies: [ + devmapper, + protobuf, + libprotobuf_mutator, + ], + link_with: [ + libcryptsetup, + libcrypto_backend, + libutils_io, + ], + link_args: [ + link_args, + ], + include_directories: includes_tools) +endif diff --git a/tests/fuzz/oss-fuzz-build.sh b/tests/fuzz/oss-fuzz-build.sh index b2f643f..cf0cfde 100755 --- a/tests/fuzz/oss-fuzz-build.sh +++ b/tests/fuzz/oss-fuzz-build.sh @@ -42,14 +42,16 @@ in_oss_fuzz && LIBFUZZER_PATCH="$PWD/cryptsetup/tests/fuzz/unpoison-mutated-buff in_oss_fuzz && apt-get update && apt-get install -y \ make autoconf automake autopoint libtool pkg-config \ sharutils gettext expect keyutils ninja-build \ - bison + bison flex [ ! -d zlib ] && git clone --depth 1 https://github.com/madler/zlib.git [ ! -d xz ] && git clone https://git.tukaani.org/xz.git [ ! -d json-c ] && git clone --depth 1 https://github.com/json-c/json-c.git -[ ! -d lvm2 ] && git clone --depth 1 https://sourceware.org/git/lvm2.git +[ ! -d lvm2 ] && git clone --depth 1 https://gitlab.com/lvmteam/lvm2 [ ! -d popt ] && git clone --depth 1 https://github.com/rpm-software-management/popt.git -[ ! -d libprotobuf-mutator ] && git clone --depth 1 https://github.com/google/libprotobuf-mutator.git \ +# FIXME: temporary fix until libprotobuf stops shuffling C++ requirements +# [ ! -d libprotobuf-mutator ] && git clone --depth 1 https://github.com/google/libprotobuf-mutator.git \ +[ ! -d libprotobuf-mutator ] && git clone --depth 1 --branch v1.1 https://github.com/google/libprotobuf-mutator.git \ && [ "$SANITIZER" == "memory" ] && ( cd libprotobuf-mutator; patch -p1 < $LIBFUZZER_PATCH ) [ ! -d openssl ] && git clone --depth 1 https://github.com/openssl/openssl [ ! -d util-linux ] && git clone --depth 1 https://github.com/util-linux/util-linux @@ -76,8 +78,8 @@ make install cd .. cd xz -./autogen.sh --no-po4a -./configure --prefix="$DEPS_PATH" --enable-static --disable-shared +./autogen.sh --no-po4a --no-doxygen +./configure --prefix="$DEPS_PATH" --enable-static --disable-shared --disable-ifunc --disable-sandbox make -j make install cd .. @@ -94,16 +96,14 @@ cd ../.. cd lvm2 ./configure --prefix="$DEPS_PATH" --enable-static_link --disable-udev_sync --enable-pkgconfig --disable-selinux make -j libdm.device-mapper -# build of dmsetup.static is broken -# make install_device-mapper -cp ./libdm/ioctl/libdevmapper.a "$DEPS_PATH"/lib/ -cp ./libdm/libdevmapper.h "$DEPS_PATH"/include/ -cp ./libdm/libdevmapper.pc "$PKG_CONFIG_PATH" +make -C libdm install_static install_pkgconfig install_include cd .. cd popt # --no-undefined is incompatible with sanitizers sed -i -e 's/-Wl,--no-undefined //' src/CMakeLists.txt +# force static build of popt +sed -i 's/add_library(popt SHARED/add_library(popt STATIC/' src/CMakeLists.txt mkdir -p build rm -fr build/* cd build diff --git a/tests/fuzz/plain_json_proto_to_luks2.cc b/tests/fuzz/plain_json_proto_to_luks2.cc index 8c56c15..a0f02c5 100644 --- a/tests/fuzz/plain_json_proto_to_luks2.cc +++ b/tests/fuzz/plain_json_proto_to_luks2.cc @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 protobuf to image converter * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/tests/fuzz/plain_json_proto_to_luks2_converter.cc b/tests/fuzz/plain_json_proto_to_luks2_converter.cc index 823c0c5..6f756a9 100644 --- a/tests/fuzz/plain_json_proto_to_luks2_converter.cc +++ b/tests/fuzz/plain_json_proto_to_luks2_converter.cc @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 custom mutator fuzz target * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,6 +32,8 @@ namespace json_proto { void LUKS2ProtoConverter::emit_luks2_binary_header(const LUKS2_header &header_proto, int fd, uint64_t offset, uint64_t seqid, const std::string &json_text) { struct luks2_hdr_disk hdr = {}; + size_t hdr_json_area_len, write_size; + uint8_t csum[LUKS2_CHECKSUM_L]; int r; if (hd) @@ -63,7 +65,6 @@ void LUKS2ProtoConverter::emit_luks2_binary_header(const LUKS2_header &header_pr strncpy(hdr.uuid, "af7f64ea-3233-4581-946b-6187d812841e", LUKS2_UUID_L); memset(hdr.salt, 1, LUKS2_SALT_L); - if (header_proto.has_selected_offset()) hdr.hdr_offset = cpu_to_be64(header_proto.selected_offset()); else @@ -74,10 +75,13 @@ void LUKS2ProtoConverter::emit_luks2_binary_header(const LUKS2_header &header_pr if (crypt_hash_write(hd, (char*)&hdr, LUKS2_HDR_BIN_LEN)) err(EXIT_FAILURE, "crypt_hash_write failed"); - size_t hdr_json_area_len = header_proto.hdr_size() - LUKS2_HDR_BIN_LEN; - uint8_t csum[LUKS2_CHECKSUM_L]; + if (header_proto.hdr_size() <= LUKS2_HDR_BIN_LEN || + header_proto.hdr_size() > LUKS2_DEFAULT_HDR_SIZE) + hdr_json_area_len = LUKS2_DEFAULT_HDR_SIZE - LUKS2_HDR_BIN_LEN; + else + hdr_json_area_len = header_proto.hdr_size() - LUKS2_HDR_BIN_LEN; - size_t write_size = json_text.length() > hdr_json_area_len - 1 ? hdr_json_area_len - 1 : json_text.length(); + write_size = json_text.length() > hdr_json_area_len - 1 ? hdr_json_area_len - 1 : json_text.length(); if (write_buffer(fd, json_text.c_str(), write_size) != (ssize_t)write_size) err(EXIT_FAILURE, "write_buffer failed"); if (crypt_hash_write(hd, json_text.c_str(), write_size)) @@ -113,6 +117,9 @@ void LUKS2ProtoConverter::convert(const LUKS2_both_headers &headers, int fd) { size_t out_size = headers.primary_header().hdr_size() + headers.secondary_header().hdr_size(); + if (out_size < 4096 || out_size > 2 * LUKS2_DEFAULT_HDR_SIZE) + out_size = LUKS2_DEFAULT_HDR_SIZE; + if (!write_headers_only) out_size += KEYSLOTS_SIZE + DATA_SIZE; diff --git a/tests/fuzz/plain_json_proto_to_luks2_converter.h b/tests/fuzz/plain_json_proto_to_luks2_converter.h index 7decf9f..aa1b594 100644 --- a/tests/fuzz/plain_json_proto_to_luks2_converter.h +++ b/tests/fuzz/plain_json_proto_to_luks2_converter.h @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 custom mutator fuzz target * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/tests/fuzz/proto_to_luks2.cc b/tests/fuzz/proto_to_luks2.cc index 4a27cad..720d25b 100644 --- a/tests/fuzz/proto_to_luks2.cc +++ b/tests/fuzz/proto_to_luks2.cc @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 protobuf to image converter * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/tests/fuzz/proto_to_luks2_converter.cc b/tests/fuzz/proto_to_luks2_converter.cc index 96a70b7..10f2b83 100644 --- a/tests/fuzz/proto_to_luks2_converter.cc +++ b/tests/fuzz/proto_to_luks2_converter.cc @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 custom mutator fuzz target * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/tests/fuzz/proto_to_luks2_converter.h b/tests/fuzz/proto_to_luks2_converter.h index 9f926d0..5547ca7 100644 --- a/tests/fuzz/proto_to_luks2_converter.h +++ b/tests/fuzz/proto_to_luks2_converter.h @@ -1,8 +1,8 @@ /* * cryptsetup LUKS2 custom mutator fuzz target * - * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com> - * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2022-2024 Daniel Zatovic <daniel.zatovic@gmail.com> + * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License |