diff --git a/evaluation-libraries/.gitignore b/evaluation-libraries/.gitignore new file mode 100644 index 0000000..e4161f6 --- /dev/null +++ b/evaluation-libraries/.gitignore @@ -0,0 +1,4 @@ +*.vscode +build/ +java/client/bin/Client.class +java/server/bin/Server.class diff --git a/evaluation-libraries/README.md b/evaluation-libraries/README.md new file mode 100644 index 0000000..8b7b943 --- /dev/null +++ b/evaluation-libraries/README.md @@ -0,0 +1,42 @@ +# evaluation-libraries +TLS-library examples with strict SNI and strict ALPN implemented to prevent the cross-protocol attacks demonstrated in the [ALPACA-Attack](https://alpaca-attack.com/index.html). + +DISCLAIMER: The implementations only focused on the ALPN&SNI TLS-Extensions, i can't guarantee that they are otherwise securely implemented. + +## Containers +Each library example starts the following containers +- ``server`` with SNI=tls-server.com , ALPN=http/1.1 written in the library +- ``server-openssl-wrong-cn`` with SNI=tls-server.com , ALPN=http/1.1 and a certificate that has a wrong common name +- ``server-openssl-malicious-alpn`` with SNI=tls-server.com and always sends back ALPN=invalid +- ``client`` runs a bash script that does the following tests + +## Tests +1. send correct SNI and ALPN to ``server`` and send application data +2. send wrong SNI to ``server`` (tests SNI on server) +3. send wrong ALPN to ``server`` (tests ALPN on server) +4. send correct SNI and ALPN to ``server-openssl-wrong-cn`` (tests strict SNI on client) +5. send correct SNI and ALPN to ``server-openssl-malicious-alpn`` (tests strict ALPN on client) + +The first test needs to succeed and every other tests needs to return a non-null value. + +## How to run +Requires docker, docker-compose and easy-rsa + +This builds all containers, runs all test and puts the results in a file called ``results`` +``` +./run-everything.sh +``` + +---------------- +### Running single libraries +First build the baseimage and the openssl image. (The openssl image is required for tests 4 and 5) +``` +cd baseimage && ./build.sh && cd .. +cd openssl && ./build.sh && cd .. +``` + +Then go into any of the library folders and start the tests +``` +./run.sh +``` + diff --git a/evaluation-libraries/baseimage/Dockerfile b/evaluation-libraries/baseimage/Dockerfile new file mode 100644 index 0000000..2591709 --- /dev/null +++ b/evaluation-libraries/baseimage/Dockerfile @@ -0,0 +1,28 @@ +ARG VERSION=3.15 +FROM alpine:${VERSION} +RUN apk add \ + git \ + linux-headers \ + cmake \ + make \ + wget \ + bash \ + autoconf \ + automake \ + coreutils \ + patch \ + gettext-dev \ + gperf \ + pkgconf \ + libtool \ + g++ \ + gcc \ + perl \ + python3 \ + go +COPY ./certs/ca.crt /etc/ssl/certs/ +COPY ./certs /etc/ssl/cert-data +COPY client.sh /client.sh +RUN mkdir /src +RUN mkdir /build +WORKDIR /src/ diff --git a/evaluation-libraries/baseimage/Dockerfile-archlinux b/evaluation-libraries/baseimage/Dockerfile-archlinux new file mode 100644 index 0000000..959490b --- /dev/null +++ b/evaluation-libraries/baseimage/Dockerfile-archlinux @@ -0,0 +1,17 @@ +FROM archlinux:base-devel +RUN pacman-key --init +RUN pacman-key --populate archlinux +RUN pacman -Syu --noconfirm +RUN pacman -S git --noconfirm + +#create build user that has root access because archlinux doesn't allow makepkg to be run as root +RUN useradd --no-create-home --shell=/bin/false build && usermod -L build +RUN echo "build ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers +RUN echo "root ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +COPY ./certs/ca.crt /etc/ssl/certs/ +COPY ./certs /etc/ssl/cert-data +COPY client.sh /client.sh +RUN mkdir /src +WORKDIR /src/ +RUN chown build /src \ No newline at end of file diff --git a/evaluation-libraries/baseimage/Dockerfile-debian b/evaluation-libraries/baseimage/Dockerfile-debian new file mode 100644 index 0000000..fec0501 --- /dev/null +++ b/evaluation-libraries/baseimage/Dockerfile-debian @@ -0,0 +1,26 @@ +ARG VERSION=bullseye +FROM debian:${VERSION} +RUN apt-get update && apt-get install -y \ + git \ + cmake \ + make \ + wget \ + bash \ + autoconf \ + automake \ + coreutils \ + patch \ + gperf \ + pkgconf \ + libtool \ + g++ \ + gcc \ + perl \ + python3 \ + golang +COPY ./certs/ca.crt /etc/ssl/certs/ +COPY ./certs /etc/ssl/cert-data +COPY client.sh /client.sh +RUN mkdir /src +RUN mkdir /build +WORKDIR /src/ diff --git a/evaluation-libraries/baseimage/build.sh b/evaluation-libraries/baseimage/build.sh new file mode 100755 index 0000000..588061d --- /dev/null +++ b/evaluation-libraries/baseimage/build.sh @@ -0,0 +1,6 @@ +(cd certs + ./generate-ca.sh); + +docker build -t tls-baseimage . +docker build -t tls-baseimagedebian -f Dockerfile-debian . +docker build -t tls-baseimage-archlinux -f Dockerfile-archlinux . diff --git a/evaluation-libraries/baseimage/certs/generate-ca.sh b/evaluation-libraries/baseimage/certs/generate-ca.sh new file mode 100755 index 0000000..309decf --- /dev/null +++ b/evaluation-libraries/baseimage/certs/generate-ca.sh @@ -0,0 +1,71 @@ +DIR="`pwd`/`dirname "$0"`/" + +echo $DIR + +if [ "$OS" = "Darwin" ]; then + brew install easy-rsa +else + apt-get install -y easy-rsa +fi + +path="/usr/share/easy-rsa/" +if [ "$OS" = "Darwin" ]; then + path="" + DIR_MAC="/usr/local/etc/" +fi +echo -e "${GREEN}[CERT] Creating PKI${NC}" +${path}easyrsa init-pki --pki-dir = "$DIR/pki" +cat << EOF > "$DIR/pki/vars" +set_var EASYRSA_DN "cn_only" +set_var EASYRSA_DIGEST "sha512" +set_var EASYRSA_BATCH "1" +set_var EASYRSA_REQ_CN "alpaca.poc" +EOF +dd if=/dev/urandom of="$DIR/pki/.rnd" bs=256 count=1 2> /dev/null +echo -e "${GREEN}[CERT] Build CA${NC}" +${path}easyrsa build-ca nopass + +echo -e "${GREEN}[CERT] Generating Certificates${NC}" +${path}easyrsa --req-cn="tls-server.com" gen-req tls-server.com nopass +${path}easyrsa sign-req server tls-server.com + +${path}easyrsa --req-cn="wrong-cn.com" gen-req wrong-cn.com nopass +${path}easyrsa sign-req server wrong-cn.com + +#copy certs +cp "$DIR/pki/issued/tls-server.com.crt" "$DIR" +cp "$DIR/pki/private/tls-server.com.key" "$DIR" +cp "$DIR/pki/issued/wrong-cn.com.crt" "$DIR" +cp "$DIR/pki/private/wrong-cn.com.key" "$DIR" +cp "$DIR/pki/ca.crt" "$DIR" + +#generate chains +cat "$DIR/tls-server.com.crt" >> "$DIR/tls-server.com-chain.crt" +cat "$DIR/ca.crt" >> "$DIR/tls-server.com-chain.crt" + +#generate chains +cat "$DIR/wrong-cn.com.crt" >> "$DIR/wrong-cn.com-chain.crt" +cat "$DIR/ca.crt" >> "$DIR/wrong-cn.com-chain.crt" + +#generate p12 +openssl pkcs12 -export -in "$DIR/tls-server.com.crt" -inkey "$DIR/tls-server.com.key" -out "$DIR/tls-server.com.p12" -password pass:123456 +openssl pkcs12 -export -in "$DIR/wrong-cn.com.crt" -inkey "$DIR/wrong-cn.com.key" -out "$DIR/wrong-cn.com.p12" -password pass:123456 + +openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in tls-server.com.key -out tls-server.com.pkcs8.key +openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in wrong-cn.com.key -out wrong-cn.com.pkcs8.key + +#if [ "$OS" = "Darwin" ]; then +# DIR_MAC="/usr/local/etc" +#else +# DIR_MAC=${DIR} +#fi +# +#mkdir -p "$DIR/servers/files/cert/" 2> /dev/null +#cp "$DIR_MAC/pki/issued/attacker.com.crt" "$DIR/servers/files/cert/" +#cp "$DIR_MAC/pki/private/attacker.com.key" "$DIR/servers/files/cert/" +# +#${path}easyrsa --req-cn="target.com" gen-req target.com nopass +#${path}easyrsa sign-req server target.com +# +#cp "$DIR_MAC/pki/issued/target.com.crt" "$DIR/servers/files/cert/" +#cp "$DIR_MAC/pki/private/target.com.key" "$DIR/servers/files/cert/" diff --git a/evaluation-libraries/baseimage/client.sh b/evaluation-libraries/baseimage/client.sh new file mode 100755 index 0000000..82acc71 --- /dev/null +++ b/evaluation-libraries/baseimage/client.sh @@ -0,0 +1,57 @@ +#!/bin/bash +#$1 command to run +#$2 server1 to connect +#$3 server2 to connect +#$4 openssl-malicious-alpn server +#$5 wait seconds before starting + +results=() + +sleep $5 + +echo "------------ Test 1: SNI=tls-server.com ALPN=http/1.1 ------------------" +$1 -h $2 -s tls-server.com -a http/1.1 +results+=($?) + +echo "------------ Test 2: SNI=example.com ALPN=http/1.1 ------------------" +/openssl-client -h $2 -s example.com -a http/1.1 +results+=($?) + +echo "------------ Test 3: SNI=tls-server.com ALPN=invalid ------------------" +/openssl-client -h $2 -s tls-server.com -a invalid +results+=($?) + +echo "------------ Test 4: wrong certificate by server ------------------" +$1 -h $3 -s tls-server.com -a http/1.1 +results+=($?) + +echo "------------ Test 5: server sends wrong alpn ------------------" +$1 -h $4 -s tls-server.com -a http/1.1 +results+=($?) + +RED='\033[0;31m ' +GREEN='\033[0;32m ' +NC='\033[0m' # No Color + +echo "" > results + +for i in "${!results[@]}"; do + test=$((i+1)) + if [ $i = "0" ]; then #first test needs to return 0 + if [ ${results[$i]} = "0" ]; + then + echo -e "${GREEN}Test$test success! exitcode:${results[$i]}" >> results; + else + echo -e "${RED}Test$test FAILED! exitcode:${results[$i]}" >> results; + fi + else #every other test needs to return non-zero value + if [ ${results[$i]} = "0" ]; + then + echo -e "${RED}Test$test FAILED! exitcode:${results[$i]}" >> results; + else + echo -e "${GREEN}Test$test success! exitcode:${results[$i]}" >> results; + fi + fi +done + +cat results \ No newline at end of file diff --git a/evaluation-libraries/bearssl/CMakeLists.txt b/evaluation-libraries/bearssl/CMakeLists.txt new file mode 100644 index 0000000..300646c --- /dev/null +++ b/evaluation-libraries/bearssl/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-bearssl VERSION 0.1.0) + + + +add_subdirectory(client) +add_subdirectory(server) diff --git a/evaluation-libraries/bearssl/Dockerfile b/evaluation-libraries/bearssl/Dockerfile new file mode 100644 index 0000000..f37a0ad --- /dev/null +++ b/evaluation-libraries/bearssl/Dockerfile @@ -0,0 +1,38 @@ +# syntax=docker/dockerfile:1 +FROM tls-openssl as tls-bearssl +ARG VERSION=0.6 + +RUN apk add sed + +WORKDIR /build +RUN git clone --depth=1 --branch=v${VERSION} https://www.bearssl.org/git/BearSSL +WORKDIR /build/BearSSL +RUN make + + + + +WORKDIR /build +ADD server /build/server +ADD client /build/client +ADD CMakeLists.txt /build/CMakeLists.txt + +# generate c code from private keys and certs +RUN ls /build/server/ +RUN /build/BearSSL/build/brssl ta /etc/ssl/cert-data/ca.crt | tail -n +2 >> /build/client/client.h +RUN /build/BearSSL/build/brssl chain /etc/ssl/cert-data/tls-server.com-chain.crt | tail -n +2 >> /build/server/server.h +RUN /build/BearSSL/build/brssl skey -C /etc/ssl/cert-data/tls-server.com.key | tail -n +2 >> /build/server/server.h + +# wrong-cn.com key&cert need different variable names +RUN /build/BearSSL/build/brssl chain /etc/ssl/cert-data/wrong-cn.com-chain.crt | tail -n +2 | sed "s/\(\(CERT[01]\)\|\(CHAIN\(_LEN\)\?\)\|\(RSA\(_[DIPQ]\*\)\?\)\)/WRONG_\1/g" >> /build/server/server.h +RUN /build/BearSSL/build/brssl skey -C /etc/ssl/cert-data/wrong-cn.com.key | tail -n +2 | sed "s/\(\(CERT[01]\)\|\(CHAIN\(_LEN\)\?\)\|\(RSA\(_[DIPQ]\*\)\?\)\)/WRONG_\1/g" >> /build/server/server.h + + +RUN cmake . .. && make +RUN mv /build/server/server / +RUN mv /build/client/client / +COPY --from=tls-openssl /openssl-client /openssl-client + + +WORKDIR / +CMD ["/server"] \ No newline at end of file diff --git a/evaluation-libraries/bearssl/LICENSE b/evaluation-libraries/bearssl/LICENSE new file mode 100644 index 0000000..f5cf444 --- /dev/null +++ b/evaluation-libraries/bearssl/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2016 Thomas Pornin + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/evaluation-libraries/bearssl/README.md b/evaluation-libraries/bearssl/README.md new file mode 100644 index 0000000..e7c789b --- /dev/null +++ b/evaluation-libraries/bearssl/README.md @@ -0,0 +1,13 @@ +# bearssl example with strict sni and strict alpn + +Tested with bearSSL 0.6 + +needs tls-baseimage already in docker + +Based on BearSSL/tools/server.c and client.c + +```bash +./run.sh +``` + + diff --git a/evaluation-libraries/bearssl/build.sh b/evaluation-libraries/bearssl/build.sh new file mode 100755 index 0000000..8a1b48c --- /dev/null +++ b/evaluation-libraries/bearssl/build.sh @@ -0,0 +1 @@ +docker build --build-arg VERSION=0.6 . -t tls-bearssl -f Dockerfile diff --git a/evaluation-libraries/bearssl/client/CMakeLists.txt b/evaluation-libraries/bearssl/client/CMakeLists.txt new file mode 100644 index 0000000..2eff2b0 --- /dev/null +++ b/evaluation-libraries/bearssl/client/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.0.0) +project(client VERSION 0.1.0) + +#include_directories(${CMAKE_SOURCE_DIR}/BearSSL/inc) +add_library(brssl STATIC IMPORTED) +set_target_properties(brssl PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/BearSSL/build/libbearssl.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/BearSSL/inc" + ) +add_executable(client client.c) +target_link_libraries(client brssl) +target_compile_options(client PRIVATE -Wall -Wextra) diff --git a/evaluation-libraries/bearssl/client/client.c b/evaluation-libraries/bearssl/client/client.c new file mode 100644 index 0000000..73dce75 --- /dev/null +++ b/evaluation-libraries/bearssl/client/client.c @@ -0,0 +1,165 @@ +#include "client.h" + +int main(int argc, char *argv[]) { + // Disable buffering on stdout so docker output is shown + setbuf(stdout, NULL); + + const char *host = "127.0.0.1"; + const char *alpn = "http/1.1"; + const char *servername = "tls-server.com"; + const char *port = "4433"; + + int opt; + while ((opt = getopt(argc, argv, "a:s:h:p:")) != -1) { + switch (opt) { + case 'a': + alpn = optarg; + break; + case 's': + servername = optarg; + break; + case 'h': + host = optarg; + break; + /*case 'c': + cert = optarg; + break;*/ + case 'p': + port = optarg; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-h ip] [-p port] \n", + argv[0]); + exit(EXIT_FAILURE); + } + } + printf("Parameters alpn=%s servername=%s cert=ca.crt host=%s \n", alpn, servername, host); + + int err; + int fd; + br_ssl_client_context sc; + br_x509_minimal_context xc; + unsigned char iobuf[BR_SSL_BUFSIZE_BIDI]; + br_sslio_context ioc; + + /* + * Open the socket to the target server. + */ + fd = host_connect(host, port); + if (fd < 0) { + return EXIT_FAILURE; + } + + /* + * Initialise the client context: + * -- Use the "full" profile (all supported algorithms). + * -- The provided X.509 validation engine is initialised, with + * the hardcoded trust anchor. + */ + br_ssl_client_init_full(&sc, &xc, TAs, TAs_NUM); + + /* + * Set the I/O buffer to the provided array. We allocated a + * buffer large enough for full-duplex behaviour with all + * allowed sizes of SSL records, hence we set the last argument + * to 1 (which means "split the buffer into separate input and + * output areas"). + */ + br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1); + + /* Set TLS 1.2 */ + br_ssl_engine_set_versions(&sc.eng, BR_TLS12, BR_TLS12); + + /* set ALPN */ + const char **alpn_ptr; + alpn_ptr = malloc(sizeof(char) * strlen(alpn)); + alpn_ptr[0] = alpn; + br_ssl_engine_set_protocol_names(&sc.eng, alpn_ptr, 1); + br_ssl_engine_add_flags(&sc.eng, BR_OPT_FAIL_ON_ALPN_MISMATCH); + + /* + * Reset the client context, for a new handshake. We provide the + * target host name: it will be used for the SNI extension. The + * last parameter is 0: we are not trying to resume a session. + */ + br_ssl_client_reset(&sc, servername, 0); + + /* + * Initialise the simplified I/O wrapper context, to use our + * SSL client context, and the two callbacks for socket I/O. + */ + br_sslio_init(&ioc, &sc.eng, sock_read, &fd, sock_write, &fd); + br_sslio_flush(&ioc); + + const char *alpn_received = br_ssl_engine_get_selected_protocol(&sc.eng); + printf("ALPN negotiatiated: %s\n", alpn_received); + + // send message to server + //const char *message = "Hello from Client!"; + //br_sslio_write(&ioc, message, strlen(message)); + + for (;;) { + // send message to server + const char *message = "Hello from Client!"; + br_sslio_write(&ioc, message, strlen(message)); + br_sslio_flush(&ioc); + + // get message from server + unsigned char tmp[512]; + int rlen = br_sslio_read(&ioc, tmp, sizeof tmp); + if (rlen < 0) { + break; + } + if (rlen > 0) { + printf("%s \n", tmp); + break; + } + } + // Close the SSL connection + br_sslio_close(&ioc); + /* + * Close the socket. + */ + close(fd); + + /* + * Check whether we closed properly or not. If the engine is + * closed, then its error status allows to distinguish between + * a normal closure and a SSL error. + * + * If the engine is NOT closed, then this means that the + * underlying network socket was closed or failed in some way. + * Note that many Web servers out there do not properly close + * their SSL connections (they don't send a close_notify alert), + * which will be reported here as "socket closed without proper + * SSL termination". + */ + if (br_ssl_engine_current_state(&sc.eng) == BR_SSL_CLOSED) { + err = br_ssl_engine_last_error(&sc.eng); + if (err == 0) { + fprintf(stderr, "SSL closed.\n"); + return EXIT_SUCCESS; + } else if (err == BR_ERR_X509_BAD_SERVER_NAME) { + fprintf(stderr, "ERROR BR_ERR_X509_BAD_SERVER_NAME\n"); + return 112; + } else if (err == BR_ALERT_BAD_CERTIFICATE) { + fprintf(stderr, "ERROR BR_ALERT_BAD_CERTIFICATE\n"); + } else if (err == BR_ALERT_CERTIFICATE_UNKNOWN) { + fprintf(stderr, "ERROR BR_ALERT_CERTIFICATE_UNKNOWN\n"); + } else if (err == BR_ALERT_NO_APPLICATION_PROTOCOL) { + fprintf(stderr, "ERROR BR_ALERT_NO_APPLICATION_PROTOCOL\n"); + return 120; + } else if (err == BR_OPT_FAIL_ON_ALPN_MISMATCH) { + fprintf(stderr, "ERROR BR_OPT_FAIL_ON_ALPN_MISMATCH\n"); + return 120; + } else if (err == BR_ERR_BAD_SNI) { + fprintf(stderr, "ERROR BR_ERR_BAD_SNI\n"); + } else { + fprintf(stderr, "SSL ERROR %d\n", err); + } + return err; + } else { + fprintf(stderr, "SSL socket closed without proper termination\n"); + return EXIT_FAILURE; + } +} \ No newline at end of file diff --git a/evaluation-libraries/bearssl/client/client.h b/evaluation-libraries/bearssl/client/client.h new file mode 100644 index 0000000..eb0848e --- /dev/null +++ b/evaluation-libraries/bearssl/client/client.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Connect to the specified host and port. The connected socket is + * returned, or -1 on error. + */ +static int +host_connect(const char *host, const char *port) { + struct addrinfo hints, *si, *p; + int fd; + int err; + + memset(&hints, 0, sizeof hints); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo(host, port, &hints, &si); + if (err != 0) { + fprintf(stderr, "ERROR: getaddrinfo(): %s\n", + gai_strerror(err)); + return -1; + } + fd = -1; + for (p = si; p != NULL; p = p->ai_next) { + struct sockaddr *sa; + void *addr; + char tmp[INET6_ADDRSTRLEN + 50]; + + sa = (struct sockaddr *)p->ai_addr; + if (sa->sa_family == AF_INET) { + addr = &((struct sockaddr_in *)sa)->sin_addr; + } else if (sa->sa_family == AF_INET6) { + addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + } else { + addr = NULL; + } + if (addr != NULL) { + inet_ntop(p->ai_family, addr, tmp, sizeof tmp); + } else { + sprintf(tmp, "", + (int)sa->sa_family); + } + //fprintf(stderr, "connecting to: %s\n", tmp); + fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (fd < 0) { + perror("socket()"); + continue; + } + if (connect(fd, p->ai_addr, p->ai_addrlen) < 0) { + perror("connect()"); + close(fd); + continue; + } + break; + } + if (p == NULL) { + freeaddrinfo(si); + fprintf(stderr, "ERROR: failed to connect\n"); + return -1; + } + freeaddrinfo(si); + //fprintf(stderr, "connected.\n"); + return fd; +} + +/* + * Low-level data read callback for the simplified SSL I/O API. + */ +static int +sock_read(void *ctx, unsigned char *buf, size_t len) { + for (;;) { + ssize_t rlen; + + rlen = read(*(int *)ctx, buf, len); + if (rlen <= 0) { + if (rlen < 0 && errno == EINTR) { + continue; + } + return -1; + } + return (int)rlen; + } +} + +/* + * Low-level data write callback for the simplified SSL I/O API. + */ +static int +sock_write(void *ctx, const unsigned char *buf, size_t len) { + for (;;) { + ssize_t wlen; + + wlen = write(*(int *)ctx, buf, len); + if (wlen <= 0) { + if (wlen < 0 && errno == EINTR) { + continue; + } + return -1; + } + return (int)wlen; + } +} \ No newline at end of file diff --git a/evaluation-libraries/bearssl/docker-compose.yml b/evaluation-libraries/bearssl/docker-compose.yml new file mode 100644 index 0000000..f87f30f --- /dev/null +++ b/evaluation-libraries/bearssl/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + bearssl-server: + image: tls-bearssl + openssl-server-wrong-cn: + image: tls-openssl + command: [ "/openssl-server", "-k", "/etc/ssl/cert-data/wrong-cn.com.key", "-c" , "/etc/ssl/cert-data/wrong-cn.com-chain.crt"] + openssl-malicious-alpn: + image: tls-openssl + command: [ "/openssl-server", "-m"] + bearssl-client: + image: tls-bearssl + command: [ "./client.sh", "/client", "bearssl-server", "openssl-server-wrong-cn", "openssl-malicious-alpn" ,"1"] + depends_on: + - bearssl-server + - openssl-server-wrong-cn + - openssl-malicious-alpn diff --git a/evaluation-libraries/bearssl/run.sh b/evaluation-libraries/bearssl/run.sh new file mode 100755 index 0000000..538a631 --- /dev/null +++ b/evaluation-libraries/bearssl/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --abort-on-container-exit --exit-code-from bearssl-client --remove-orphans diff --git a/evaluation-libraries/bearssl/server/CMakeLists.txt b/evaluation-libraries/bearssl/server/CMakeLists.txt new file mode 100644 index 0000000..d1737a4 --- /dev/null +++ b/evaluation-libraries/bearssl/server/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.0.0) +project(server VERSION 0.1.0) + +#include_directories(${CMAKE_SOURCE_DIR}/BearSSL/inc) +add_library(brssl STATIC IMPORTED) +set_target_properties(brssl PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/BearSSL/build/libbearssl.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/BearSSL/inc" + ) +add_executable(server server.c) +target_link_libraries(server brssl) +target_compile_options(server PRIVATE -Wall -Wextra) \ No newline at end of file diff --git a/evaluation-libraries/bearssl/server/server.c b/evaluation-libraries/bearssl/server/server.c new file mode 100644 index 0000000..9f4b74d --- /dev/null +++ b/evaluation-libraries/bearssl/server/server.c @@ -0,0 +1,163 @@ +#include "server.h" + +#define CHECK(x) x; + +const char *servername = "tls-server.com"; + +int main(int argc, char *argv[]) { + // Disable buffering on stdout so docker output is shown + setbuf(stdout, NULL); + + const char *alpn = "http/1.1"; + const char *port = "4433"; + int wrong_certificate = 0; + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:w")) != -1) { + switch (opt) { + case 'a': + alpn = optarg; + break; + case 's': + servername = optarg; + break; + case 'w': + wrong_certificate = 1; + break; + /*case 'k': + key = optarg; + break;*/ + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-w uses wrong cert] \n", argv[0]); + return EXIT_FAILURE; + } + } + printf("Parameters alpn=%s servername=%s use_wrong_cert=%d ", alpn, servername, wrong_certificate); + + int fd; + + const char **alpn_ptr; + alpn_ptr = malloc(sizeof(char) * strlen(alpn)); + alpn_ptr[0] = alpn; + + unsigned char tmp[512]; + const char *message = "Hello from Server!"; + + int cfd; + br_ssl_server_context sc; + unsigned char iobuf[BR_SSL_BUFSIZE_BIDI]; + br_sslio_context ioc; + int err; + + /* + * Open the server socket. + */ + fd = host_bind(NULL, port); + if (fd < 0) { + return EXIT_FAILURE; + } + + /* + * Process each client, one at a time. + */ + for (;;) { + cfd = accept_client(fd); + if (cfd < 0) { + return EXIT_FAILURE; + } + + /* + * Choose certificate + * CHAIN=tls-server.com-chain.crt + * WRONG_CHAIN=wrong-cn.com-chain.crt + */ + if (wrong_certificate == 1) { + br_ssl_server_init_full_rsa(&sc, WRONG_CHAIN, WRONG_CHAIN_LEN, &WRONG_RSA); + } else { + br_ssl_server_init_full_rsa(&sc, CHAIN, CHAIN_LEN, &RSA); + } + + /* from bearssl-0.6/src/ssl/br_ssl_server_set_single_rsa.c + normally gets called by br_ssl_server_init_full_rsa that then calls br_ssl_server_set_single_rsa + + change policy handler to a custom one + + sr_choose overrides the method that processes the ClientHello and chooses certificates, cipher suited etc. + */ + static const br_ssl_server_policy_class sr_policy_vtable = { + sizeof(br_ssl_server_policy_rsa_context), + choose, + do_keyx, + do_sign}; + (&sc)->chain_handler.single_rsa.vtable = &sr_policy_vtable; + (&sc)->policy_vtable = &(&sc)->chain_handler.single_rsa.vtable; + + /* Set TLS 1.2 */ + br_ssl_engine_set_versions(&sc.eng, BR_TLS12, BR_TLS12); + + /* + * Set the I/O buffer to the provided array. We + * allocated a buffer large enough for full-duplex + * behaviour with all allowed sizes of SSL records, + * hence we set the last argument to 1 (which means + * "split the buffer into separate input and output + * areas"). + */ + br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1); + + /* set ALPN */ + + br_ssl_engine_set_protocol_names(&sc.eng, alpn_ptr, 1); + + // Enable strict ALPN + br_ssl_engine_add_flags(&sc.eng, BR_OPT_FAIL_ON_ALPN_MISMATCH); + + /* + * Reset the server context, for a new handshake. + */ + br_ssl_server_reset(&sc); + + /* + * Initialise the simplified I/O wrapper context. + */ + br_sslio_init(&ioc, &sc.eng, sock_read, &cfd, sock_write, &cfd); + br_sslio_flush(&ioc); + + for (;;) { + // get message from client + int rlen = br_sslio_read(&ioc, tmp, sizeof tmp); + if (rlen < 0) { + break; + } else { + printf("%s \n", tmp); + // send message to server + br_sslio_write(&ioc, message, strlen(message)); + br_sslio_close(&ioc); + break; + } + } + + // Check if SSL closed correctly + err = br_ssl_engine_last_error(&sc.eng); + if (err == 0) { + fprintf(stderr, "SSL closed.\n"); + } else if (err == BR_ERR_X509_BAD_SERVER_NAME) { + fprintf(stderr, "ERROR BR_ERR_X509_BAD_SERVER_NAME\n"); + } else if (err == BR_ERR_IO) { + fprintf(stderr, "BR_ERR_IO\n"); + } else if (err == BR_ALERT_BAD_CERTIFICATE) { + fprintf(stderr, "ERROR BR_ALERT_BAD_CERTIFICATE\n"); + } else if (err == BR_ALERT_CERTIFICATE_UNKNOWN) { + fprintf(stderr, "ERROR BR_ALERT_CERTIFICATE_UNKNOWN\n"); + } else if (err == BR_ALERT_NO_APPLICATION_PROTOCOL) { + fprintf(stderr, "ERROR BR_ALERT_NO_APPLICATION_PROTOCOL\n"); + } else if (err == BR_OPT_FAIL_ON_ALPN_MISMATCH) { + fprintf(stderr, "ERROR BR_OPT_FAIL_ON_ALPN_MISMATCH\n"); + } else if (err == BR_ERR_BAD_SNI) { + fprintf(stderr, "ERROR BR_ERR_BAD_SNI\n"); + } else { + fprintf(stderr, "SSL ERROR %d\n", err); + } + close(cfd); + } +} \ No newline at end of file diff --git a/evaluation-libraries/bearssl/server/server.h b/evaluation-libraries/bearssl/server/server.h new file mode 100644 index 0000000..f49659a --- /dev/null +++ b/evaluation-libraries/bearssl/server/server.h @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#include "bearssl.h" + +/* from bearssl-0.6/src/ssl/ssl_hashes.c */ +int br_ssl_choose_hash(unsigned bf) { + static const unsigned char pref[] = { + br_sha256_ID, br_sha384_ID, br_sha512_ID, + br_sha224_ID, br_sha1_ID}; + size_t u; + + for (u = 0; u < sizeof pref; u++) { + int x; + + x = pref[u]; + if ((bf >> x) & 1) { + return x; + } + } + return 0; +} + +/* from bearssl-0.6/src/ssl/br_ssl_server_set_single_rsa.c */ +static int +choose(const br_ssl_server_policy_class **pctx, + const br_ssl_server_context *cc, + br_ssl_server_choices *choices) { + br_ssl_server_policy_rsa_context *pc; + const br_suite_translated *st; + size_t u, st_num; + unsigned hash_id; + int fh; + + /* verify SNI extension */ + const char *servername_received = br_ssl_engine_get_server_name(&cc->eng); + if (strcmp("tls-server.com", servername_received) != 0) { + fprintf(stderr, "\n Invalid SNI received: %s\n", servername_received); + return 0; + } else { + printf("\n SNI received: %s\n", servername_received); + } + + //printf("workign fine %s \n", name); + + pc = (br_ssl_server_policy_rsa_context *)pctx; + st = br_ssl_server_get_client_suites(cc, &st_num); + if (cc->eng.session.version < BR_TLS12) { + hash_id = 0; + fh = 1; + } else { + hash_id = br_ssl_choose_hash( + br_ssl_server_get_client_hashes(cc)); + fh = (hash_id != 0); + } + choices->chain = pc->chain; + choices->chain_len = pc->chain_len; + for (u = 0; u < st_num; u++) { + unsigned tt; + + tt = st[u][1]; + switch (tt >> 12) { + case BR_SSLKEYX_RSA: + if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0) { + choices->cipher_suite = st[u][0]; + return 1; + } + break; + case BR_SSLKEYX_ECDHE_RSA: + if ((pc->allowed_usages & BR_KEYTYPE_SIGN) != 0 && fh) { + choices->cipher_suite = st[u][0]; + choices->algo_id = hash_id + 0xFF00; + return 1; + } + break; + } + } + return 0; +} + +static uint32_t +do_keyx(const br_ssl_server_policy_class **pctx, + unsigned char *data, size_t *len) { + br_ssl_server_policy_rsa_context *pc; + + pc = (br_ssl_server_policy_rsa_context *)pctx; + return br_rsa_ssl_decrypt(pc->irsacore, pc->sk, data, *len); +} + +/* + * OID for hash functions in RSA signatures. + */ +static const unsigned char HASH_OID_SHA1[] = { + 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A}; + +static const unsigned char HASH_OID_SHA224[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04}; + +static const unsigned char HASH_OID_SHA256[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}; + +static const unsigned char HASH_OID_SHA384[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02}; + +static const unsigned char HASH_OID_SHA512[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}; + +static const unsigned char *HASH_OID[] = { + HASH_OID_SHA1, + HASH_OID_SHA224, + HASH_OID_SHA256, + HASH_OID_SHA384, + HASH_OID_SHA512}; + +static size_t +do_sign(const br_ssl_server_policy_class **pctx, + unsigned algo_id, unsigned char *data, size_t hv_len, size_t len) { + br_ssl_server_policy_rsa_context *pc; + unsigned char hv[64]; + size_t sig_len; + const unsigned char *hash_oid; + + pc = (br_ssl_server_policy_rsa_context *)pctx; + memcpy(hv, data, hv_len); + algo_id &= 0xFF; + if (algo_id == 0) { + hash_oid = NULL; + } else if (algo_id >= 2 && algo_id <= 6) { + hash_oid = HASH_OID[algo_id - 2]; + } else { + return 0; + } + sig_len = (pc->sk->n_bitlen + 7) >> 3; + if (len < sig_len) { + return 0; + } + return pc->irsasign(hash_oid, hv, hv_len, pc->sk, data) ? sig_len : 0; +} + +/* + * This sample code can use three possible certificate chains: + * -- A full-RSA chain (server key is RSA, certificates are signed with RSA) + * -- A full-EC chain (server key is EC, certificates are signed with ECDSA) + * -- A mixed chain (server key is EC, certificates are signed with RSA) + * + * The macros below define which chain is selected. This impacts the list + * of supported cipher suites. + */ + +#if !(SERVER_RSA || SERVER_EC || SERVER_MIXED) +#define SERVER_RSA 1 +#define SERVER_EC 0 +#define SERVER_MIXED 0 +#endif + +/* + * Create a server socket bound to the specified host and port. If 'host' + * is NULL, this will bind "generically" (all addresses). + * + * Returned value is the server socket descriptor, or -1 on error. + */ +static int +host_bind(const char *host, const char *port) { + struct addrinfo hints, *si, *p; + int fd; + int err; + + memset(&hints, 0, sizeof hints); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo(host, port, &hints, &si); + if (err != 0) { + fprintf(stderr, "ERROR: getaddrinfo(): %s\n", + gai_strerror(err)); + return -1; + } + fd = -1; + for (p = si; p != NULL; p = p->ai_next) { + struct sockaddr *sa; + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + size_t sa_len; + void *addr; + char tmp[INET6_ADDRSTRLEN + 50]; + int opt; + + sa = (struct sockaddr *)p->ai_addr; + if (sa->sa_family == AF_INET) { + sa4 = *(struct sockaddr_in *)sa; + sa = (struct sockaddr *)&sa4; + sa_len = sizeof sa4; + addr = &sa4.sin_addr; + if (host == NULL) { + sa4.sin_addr.s_addr = INADDR_ANY; + } + } else if (sa->sa_family == AF_INET6) { + sa6 = *(struct sockaddr_in6 *)sa; + sa = (struct sockaddr *)&sa6; + sa_len = sizeof sa6; + addr = &sa6.sin6_addr; + if (host == NULL) { + sa6.sin6_addr = in6addr_any; + } + } else { + addr = NULL; + sa_len = p->ai_addrlen; + } + if (addr != NULL) { + inet_ntop(p->ai_family, addr, tmp, sizeof tmp); + } else { + sprintf(tmp, "", + (int)sa->sa_family); + } + //fprintf(stderr, "binding to: %s\n", tmp); + fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (fd < 0) { + perror("socket()"); + continue; + } + opt = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt); + opt = 0; + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof opt); + if (bind(fd, sa, sa_len) < 0) { + perror("bind()"); + close(fd); + continue; + } + break; + } + if (p == NULL) { + freeaddrinfo(si); + fprintf(stderr, "ERROR: failed to bind\n"); + return -1; + } + freeaddrinfo(si); + if (listen(fd, 5) < 0) { + perror("listen()"); + close(fd); + return -1; + } + //printf(stderr, "bound.\n"); + return fd; +} + +/* + * Accept a single client on the provided server socket. This is blocking. + * On error, this returns -1. + */ +static int +accept_client(int server_fd) { + int fd; + struct sockaddr sa; + socklen_t sa_len; + char tmp[INET6_ADDRSTRLEN + 50]; + const char *name; + + sa_len = sizeof sa; + fd = accept(server_fd, &sa, &sa_len); + if (fd < 0) { + perror("accept()"); + return -1; + } + name = NULL; + switch (sa.sa_family) { + case AF_INET: + name = inet_ntop(AF_INET, + &((struct sockaddr_in *)&sa)->sin_addr, + tmp, sizeof tmp); + break; + case AF_INET6: + name = inet_ntop(AF_INET, + &((struct sockaddr_in *)&sa)->sin_addr, + tmp, sizeof tmp); + break; + } + if (name == NULL) { + sprintf(tmp, "", (unsigned long)sa.sa_family); + name = tmp; + } + //fprintf(stderr, "accepting connection from: %s\n", name); + return fd; +} + +/* + * Low-level data read callback for the simplified SSL I/O API. + */ +static int +sock_read(void *ctx, unsigned char *buf, size_t len) { + for (;;) { + ssize_t rlen; + + rlen = read(*(int *)ctx, buf, len); + if (rlen <= 0) { + if (rlen < 0 && errno == EINTR) { + continue; + } + return -1; + } + return (int)rlen; + } +} + +/* + * Low-level data write callback for the simplified SSL I/O API. + */ +static int +sock_write(void *ctx, const unsigned char *buf, size_t len) { + for (;;) { + ssize_t wlen; + + wlen = write(*(int *)ctx, buf, len); + if (wlen <= 0) { + if (wlen < 0 && errno == EINTR) { + continue; + } + return -1; + } + return (int)wlen; + } +} diff --git a/evaluation-libraries/botan/CMakeLists.txt b/evaluation-libraries/botan/CMakeLists.txt new file mode 100644 index 0000000..06203de --- /dev/null +++ b/evaluation-libraries/botan/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-botan VERSION 0.1.0) + + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +include(CMakeToolsHelpers OPTIONAL) +include(FeatureSummary) + +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) +include(CheckCXXSourceCompiles) + + + +add_subdirectory(client) +add_subdirectory(server) \ No newline at end of file diff --git a/evaluation-libraries/botan/Dockerfile b/evaluation-libraries/botan/Dockerfile new file mode 100644 index 0000000..abab3ba --- /dev/null +++ b/evaluation-libraries/botan/Dockerfile @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:1 +FROM tls-baseimage as tls-botan +ARG VERSION=2.18.1 +RUN wget https://botan.randombit.net/releases/Botan-${VERSION}.tar.xz +RUN tar -xf Botan-${VERSION}.tar.xz +WORKDIR /src/Botan-${VERSION} +RUN apk add python2 +RUN ./configure.py --prefix=/build/ +RUN make +RUN make install +RUN mv libbotan-2.a /lib/libbotan-2.a +RUN mv /build/include/* /usr/include/ + +ADD cmake /build/cmake +ADD server /build/server +ADD client /build/client +ADD CMakeLists.txt /build/CMakeLists.txt +WORKDIR /build +RUN cmake . .. && make +RUN mv /build/server/server / +RUN mv /build/client/client / +COPY --from=tls-openssl /openssl-client /openssl-client +WORKDIR / +CMD ["/server"] diff --git a/evaluation-libraries/botan/LICENSE b/evaluation-libraries/botan/LICENSE new file mode 100644 index 0000000..b73156f --- /dev/null +++ b/evaluation-libraries/botan/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 1999-2021 The Botan Authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions, and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/evaluation-libraries/botan/README.md b/evaluation-libraries/botan/README.md new file mode 100644 index 0000000..2a2beab --- /dev/null +++ b/evaluation-libraries/botan/README.md @@ -0,0 +1,13 @@ +# botan example with strict sni and strict alpn + +Tested with botan 2.17.3 + +needs tls-baseimage already in docker + +Based on https://github.com/randombit/botan tls_client.cpp and tls_server.cpp + +```bash +./run.sh +``` + + diff --git a/evaluation-libraries/botan/build.sh b/evaluation-libraries/botan/build.sh new file mode 100755 index 0000000..2ffda90 --- /dev/null +++ b/evaluation-libraries/botan/build.sh @@ -0,0 +1 @@ +docker build . -t tls-botan \ No newline at end of file diff --git a/evaluation-libraries/botan/client/CMakeLists.txt b/evaluation-libraries/botan/client/CMakeLists.txt new file mode 100644 index 0000000..b091162 --- /dev/null +++ b/evaluation-libraries/botan/client/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-botan-client VERSION 0.1.0) + +find_package(Botan2 REQUIRED) +include_directories(SYSTEM ${BOTAN2_INCLUDE_DIR}) + +add_executable(client client.cpp) +target_link_libraries(client pthread ${BOTAN2_LIBRARIES}) +target_compile_options(client PRIVATE -Wall -Wextra) \ No newline at end of file diff --git a/evaluation-libraries/botan/client/client.cpp b/evaluation-libraries/botan/client/client.cpp new file mode 100644 index 0000000..ecdf6ec --- /dev/null +++ b/evaluation-libraries/botan/client/client.cpp @@ -0,0 +1,334 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef int socket_type; +typedef size_t sendrecv_len_type; + +socket_type m_sockfd; +std::string host = "127.0.0.1"; +std::string cert = "/etc/ssl/certs"; +std::string servername = "tls-server.com"; +std::vector alpn = {"http/1.1"}; +uint16_t port = 4433; + +bool message_received = false; + +socket_type connect_to_host(const std::string &host, uint16_t port, bool tcp) { + addrinfo hints; + Botan::clear_mem(&hints, 1); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = tcp ? SOCK_STREAM : SOCK_DGRAM; + addrinfo *res, *rp = nullptr; + + if (::getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res) != 0) { + std::cerr << "getaddrinfo failed for" << host << std::endl; + } + + socket_type fd = 0; + + for (rp = res; rp != nullptr; rp = rp->ai_next) { + fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if (fd == -1) { + continue; + } + + if (::connect(fd, rp->ai_addr, static_cast(rp->ai_addrlen)) != 0) { + ::close(fd); + continue; + } + + break; + } + + ::freeaddrinfo(res); + + if (rp == nullptr) // no address succeeded + { + std::cerr << "connect failed" << std::endl; + exit(EXIT_FAILURE); + } + + return fd; +} + +class Basic_TLS_Policy final : public Botan::TLS::Policy { + public: + bool require_cert_revocation_info() const override { + return false; + } + bool acceptable_protocol_version(Botan::TLS::Protocol_Version version) const override { + if (version == Botan::TLS::Protocol_Version::TLS_V12 && allow_tls12()) + return true; + return false; + } +}; + +class Basic_Credentials_Manager : public Botan::Credentials_Manager { + public: + Basic_Credentials_Manager(const std::string &ca_path) { + if (ca_path.empty() == false) { + m_certstores.push_back(std::make_shared(ca_path)); + } + } + + Basic_Credentials_Manager() { + m_certstores.push_back(std::make_shared()); + } + + Basic_Credentials_Manager(const std::string &server_crt, const std::string &server_key) { + Certificate_Info cert; + + Botan::DataSource_Stream key_in(server_key); + cert.key = Botan::PKCS8::load_key(key_in); + + Botan::DataSource_Stream in(server_crt); + while (!in.end_of_data()) { + try { + cert.certs.push_back(Botan::X509_Certificate(in)); + } catch (std::exception &) { + } + } + m_creds.push_back(cert); + } + + std::vector + trusted_certificate_authorities(const std::string &type, + const std::string & /*hostname*/) override { + std::vector v; + + // don't ask for client certs + if (type == "tls-server.com") { + return v; + } + + for (auto const &cs : m_certstores) { + v.push_back(cs.get()); + } + + return v; + } + + std::vector cert_chain( + const std::vector &algos, + const std::string &type, + const std::string &hostname) override { + BOTAN_UNUSED(type); + + for (auto const &i : m_creds) { + if (std::find(algos.begin(), algos.end(), i.key->algo_name()) == algos.end()) { + continue; + } + + if (hostname != "" && !i.certs[0].matches_dns_name(hostname)) { + continue; + } + + return i.certs; + } + + return std::vector(); + } + + Botan::Private_Key *private_key_for(const Botan::X509_Certificate &cert, + const std::string & /*type*/, + const std::string & /*context*/) override { + for (auto const &i : m_creds) { + if (cert == i.certs[0]) { + return i.key.get(); + } + } + + return nullptr; + } + + public: + struct Certificate_Info { + std::vector certs; + std::shared_ptr key; + }; + + std::vector m_creds; + std::vector> m_certstores; +}; + +/** + * @brief Callbacks invoked by TLS::Channel. + * + * Botan::TLS::Callbacks is an abstract class. + * For improved readability, only the functions that are mandatory + * to implement are listed here. See src/lib/tls/tls_callbacks.h. + */ +class Callbacks : public Botan::TLS::Callbacks { + public: + void tls_emit_data(const uint8_t buf[], size_t length) override { + size_t offset = 0; + + while (length) { + ssize_t sent = ::send(m_sockfd, buf + offset, length, MSG_NOSIGNAL); + + if (sent == -1) { + if (errno == EINTR) { + sent = 0; + } else { + std::cerr << "Socket write failed errno=" << std::to_string(errno) << std::endl; + exit(EXIT_FAILURE); + } + } + + offset += sent; + length -= sent; + } + } + void tls_record_received(uint64_t /*seq_no*/, const uint8_t input[], size_t input_len) override { + for (size_t i = 0; i != input_len; ++i) { + std::cout << input[i]; + } + std::cout << std::endl; + message_received = true; + } + + void tls_alert(Botan::TLS::Alert alert) override { + std::cerr << "Alert: " << alert.type_string() << "\n"; + if (alert.is_fatal()) { + exit(alert.type()); + } + } + + bool tls_session_established(const Botan::TLS::Session &session) override { + return true; + } +}; + +int main(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "a:s:c:h:d")) != -1) { + switch (opt) { + case 'a': + alpn[0] = optarg; + break; + case 's': + servername = optarg; + break; + case 'h': + host = optarg; + break; + case 'c': + cert = optarg; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-c CAfolder] [-h ip] \n", + argv[0]); + exit(EXIT_FAILURE); + } + } + std::cout << "Parameters alpn=" << alpn[0] << " servername=" << servername << " cert=" << cert << std::endl; + + struct sockaddr_storage addrbuf; + m_sockfd = -1; + std::string hostname; + if (!host.empty() && + inet_pton(AF_INET, host.c_str(), &addrbuf) != 1 && + inet_pton(AF_INET6, host.c_str(), &addrbuf) != 1) { + hostname = host; + } + + m_sockfd = connect_to_host(host, port, true); + + Basic_Credentials_Manager creds(cert); + Basic_TLS_Policy policy; + Callbacks callbacks; + Botan::AutoSeeded_RNG rng; + Botan::TLS::Session_Manager_In_Memory session_mgr(rng); + + // open the tls connection + Botan::TLS::Client client(callbacks, + session_mgr, + creds, + policy, + rng, + Botan::TLS::Server_Information(servername, port), + Botan::TLS::Protocol_Version::TLS_V12, alpn); + + bool first_active = true; + + while (!client.is_closed()) { + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(m_sockfd, &readfds); + + // Send Application Data + if (client.is_active() && first_active) { + if (!alpn.empty()) { + std::string app = client.application_protocol(); + if (app.compare(alpn[0]) != 0) { + std::cout << "INVALID ALPN: " << client.application_protocol() << "\n"; + exit(120); + } + std::cout << "ALPN: " << client.application_protocol() << "\n"; + } + + /* Send Message to Server */ + std::string message = "Hello from Client!\n"; + std::vector myVector(message.begin(), message.end()); + client.send(myVector.data(), message.size()); + + first_active = false; + //client.close(); + //break; + } + if (client.is_active() && message_received) { + client.close(); + } + if (FD_ISSET(m_sockfd, &readfds)) { + uint8_t buf[4 * 1024] = {0}; + + ssize_t got = ::read(m_sockfd, buf, sizeof(buf)); + + if (got == 0) { + std::cout << "EOF on socket\n"; + break; + } else if (got == -1) { + std::cout << "Socket error: " << errno << " " << std::strerror(errno) << "\n"; + exit(EXIT_FAILURE); + continue; + } + + try { + client.received_data(buf, got); + } catch (Botan::TLS::TLS_Exception &e) { + std::cout << e.what() << std::endl; + client.close(); + exit(e.error_code()); + } + } + if (client.timeout_check()) { + std::cout << "Timeout detected\n"; + } + } + ::close(m_sockfd); +} diff --git a/evaluation-libraries/botan/cmake/FindBotan2.cmake b/evaluation-libraries/botan/cmake/FindBotan2.cmake new file mode 100644 index 0000000..2503f17 --- /dev/null +++ b/evaluation-libraries/botan/cmake/FindBotan2.cmake @@ -0,0 +1,130 @@ +# Copyright (c) 2018-2020 Ribose Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +#.rst: +# FindBotan2 +# ----------- +# +# Find the botan-2 library. +# +# IMPORTED Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines :prop_tgt:`IMPORTED` targets: +# +# ``Botan2::Botan2`` +# The botan-2 library, if found. +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following variables: +# +# :: +# +# BOTAN2_FOUND - true if the headers and library were found +# BOTAN2_INCLUDE_DIRS - where to find headers +# BOTAN2_LIBRARIES - list of libraries to link +# BOTAN2_VERSION - library version that was found, if any + +# use pkg-config to get the directories and then use these values +# in the find_path() and find_library() calls +find_package(PkgConfig QUIET) +pkg_check_modules(PC_BOTAN2 QUIET botan-2) + +# find the headers +find_path(BOTAN2_INCLUDE_DIR + NAMES botan/version.h + HINTS + ${PC_BOTAN2_INCLUDEDIR} + ${PC_BOTAN2_INCLUDE_DIRS} + PATH_SUFFIXES botan-2 +) + +# find the library +if(MSVC) + find_library(BOTAN2_LIBRARY + NAMES botan + HINTS + ${PC_BOTAN2_LIBDIR} + ${PC_BOTAN2_LIBRARY_DIRS} + ) +else() + find_library(BOTAN2_LIBRARY + NAMES botan-2 libbotan-2 + HINTS + ${PC_BOTAN2_LIBDIR} + ${PC_BOTAN2_LIBRARY_DIRS} + ) +endif() + +# determine the version +if(PC_BOTAN2_VERSION) + set(BOTAN2_VERSION ${PC_BOTAN2_VERSION}) +elseif(BOTAN2_INCLUDE_DIR AND EXISTS "${BOTAN2_INCLUDE_DIR}/botan/build.h") + file(STRINGS "${BOTAN2_INCLUDE_DIR}/botan/build.h" botan2_version_str + REGEX "^#define[\t ]+(BOTAN_VERSION_[A-Z]+)[\t ]+[0-9]+") + + string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MAJOR[\t ]+([0-9]+).*" + "\\1" _botan2_version_major "${botan2_version_str}") + string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MINOR[\t ]+([0-9]+).*" + "\\1" _botan2_version_minor "${botan2_version_str}") + string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_PATCH[\t ]+([0-9]+).*" + "\\1" _botan2_version_patch "${botan2_version_str}") + set(BOTAN2_VERSION "${_botan2_version_major}.${_botan2_version_minor}.${_botan2_version_patch}" + CACHE INTERNAL "The version of Botan which was detected") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Botan2 + REQUIRED_VARS BOTAN2_LIBRARY BOTAN2_INCLUDE_DIR + VERSION_VAR BOTAN2_VERSION +) + +if (BOTAN2_FOUND) + set(BOTAN2_INCLUDE_DIRS ${BOTAN2_INCLUDE_DIR} ${PC_BOTAN2_INCLUDE_DIRS}) + set(BOTAN2_LIBRARIES ${BOTAN2_LIBRARY}) +endif() + +if (BOTAN2_FOUND AND NOT TARGET Botan2::Botan2) + # create the new library target + add_library(Botan2::Botan2 UNKNOWN IMPORTED) + # set the required include dirs for the target + if (BOTAN2_INCLUDE_DIRS) + set_target_properties(Botan2::Botan2 + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${BOTAN2_INCLUDE_DIRS}" + ) + endif() + # set the required libraries for the target + if (EXISTS "${BOTAN2_LIBRARY}") + set_target_properties(Botan2::Botan2 + PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${BOTAN2_LIBRARY}" + ) + endif() +endif() + +mark_as_advanced(BOTAN2_INCLUDE_DIR BOTAN2_LIBRARY) \ No newline at end of file diff --git a/evaluation-libraries/botan/docker-compose.yml b/evaluation-libraries/botan/docker-compose.yml new file mode 100644 index 0000000..c360815 --- /dev/null +++ b/evaluation-libraries/botan/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + botan-server: + image: tls-botan + openssl-server-wrong-cn: + image: tls-openssl + command: [ "/openssl-server", "-k", "/etc/ssl/cert-data/wrong-cn.com.key", "-c" , "/etc/ssl/cert-data/wrong-cn.com-chain.crt"] + openssl-malicious-alpn: + image: tls-openssl + command: [ "/openssl-server", "-m"] + botan-client: + image: tls-botan + command: [ "/client.sh", "/client", "botan-server", "openssl-server-wrong-cn", "openssl-malicious-alpn" ,"1"] + depends_on: + - botan-server + - openssl-server-wrong-cn + - openssl-malicious-alpn \ No newline at end of file diff --git a/evaluation-libraries/botan/run.sh b/evaluation-libraries/botan/run.sh new file mode 100755 index 0000000..8e38d8e --- /dev/null +++ b/evaluation-libraries/botan/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --abort-on-container-exit --exit-code-from botan-client --remove-orphans \ No newline at end of file diff --git a/evaluation-libraries/botan/server/CMakeLists.txt b/evaluation-libraries/botan/server/CMakeLists.txt new file mode 100644 index 0000000..bb1145d --- /dev/null +++ b/evaluation-libraries/botan/server/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-botan-server VERSION 0.1.0) + +find_package(Botan2 REQUIRED) +include_directories(SYSTEM ${BOTAN2_INCLUDE_DIR}) + +add_executable(server server.cpp) +target_link_libraries(server pthread ${BOTAN2_LIBRARIES}) +target_compile_options(server PRIVATE -Wall -Wextra) \ No newline at end of file diff --git a/evaluation-libraries/botan/server/server.cpp b/evaluation-libraries/botan/server/server.cpp new file mode 100644 index 0000000..59e4d23 --- /dev/null +++ b/evaluation-libraries/botan/server/server.cpp @@ -0,0 +1,328 @@ +#include "server.h" + +std::string host = "127.0.0.1"; +std::string servername = "tls-server.com"; +std::vector alpn = {"http/1.1"}; +uint16_t port = 4433; + +std::string cert = "/etc/ssl/cert-data/tls-server.com.crt"; +std::string key = "/etc/ssl/cert-data/tls-server.com.pkcs8.key"; +size_t max_clients = 5; +std::string transport = "tcp"; + +bool message_sent = false; +bool message_received = false; + +std::list m_pending_output; +std::string m_line_buf; +socket_type m_socket = -1; +size_t clients_served = 0; + +class Basic_Credentials_Manager : public Botan::Credentials_Manager { + public: + Basic_Credentials_Manager(const std::string &ca_path) { + if (ca_path.empty() == false) { + m_certstores.push_back(std::make_shared(ca_path)); + } + } + + Basic_Credentials_Manager(const std::string &server_crt, const std::string &server_key) { + Certificate_Info cert; + + Botan::DataSource_Stream key_in(server_key); + cert.key = Botan::PKCS8::load_key(key_in); + + Botan::DataSource_Stream in(server_crt); + while (!in.end_of_data()) { + try { + cert.certs.push_back(Botan::X509_Certificate(in)); + } catch (std::exception &) { + } + } + + // TODO: attempt to validate chain ourselves + + m_creds.push_back(cert); + } + + std::vector find_cert_chain( + const std::vector &key_types, + const std::vector &, + const std::string &type, + const std::string &context) override { + return cert_chain(key_types, type, context); + } + + std::vector + trusted_certificate_authorities(const std::string &type, + const std::string & /*hostname*/) override { + std::vector v; + + // don't ask for client certs + if (type == "tls-server.com") { + return v; + } + + for (auto const &cs : m_certstores) { + v.push_back(cs.get()); + } + + return v; + } + + std::vector cert_chain( + const std::vector &algos, + const std::string &type, + const std::string &hostname) override { + BOTAN_UNUSED(type); + + if (servername.compare(hostname) != 0) { + throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::UNRECOGNIZED_NAME, "INVALID SNI"); + } else { + std::cout << "SNI: " << servername << std::endl; + } + + for (auto const &i : m_creds) { + if (std::find(algos.begin(), algos.end(), i.key->algo_name()) == algos.end()) { + continue; + } + + if (hostname != "" && !i.certs[0].matches_dns_name(hostname)) { + continue; + } + + return i.certs; + } + + return std::vector(); + } + + std::vector cert_chain_single_type( + const std::string &cert_key_type, + const std::string &type, + const std::string &context) { + std::vector cert_types; + cert_types.push_back(cert_key_type); + std::cout << context << std::endl; + return find_cert_chain(cert_types, std::vector(), type, context); + } + + Botan::Private_Key *private_key_for(const Botan::X509_Certificate &cert, + const std::string & /*type*/, + const std::string & /*context*/) override { + for (auto const &i : m_creds) { + if (cert == i.certs[0]) { + return i.key.get(); + } + } + + return nullptr; + } + + public: + struct Certificate_Info { + std::vector certs; + std::shared_ptr key; + }; + + std::vector m_creds; + std::vector> m_certstores; +}; + +class Basic_TLS_Policy final : public Botan::TLS::Policy { + public: + bool require_cert_revocation_info() const override { + return false; + } + bool acceptable_protocol_version(Botan::TLS::Protocol_Version version) const override { + if (version == Botan::TLS::Protocol_Version::TLS_V12 && allow_tls12()) + return true; + return false; + } +}; + +class Callbacks : public Botan::TLS::Callbacks { + public: + bool tls_session_established(const Botan::TLS::Session &session) override { + //std::cout << "Handshake complete, " << session.version().to_string() + // << " using " << session.ciphersuite().to_string() << std::endl; + + if (!session.session_id().empty()) { + //std::cout << "Session ID " << Botan::hex_encode(session.session_id()) << std::endl; + } + + if (!session.session_ticket().empty()) { + std::cout << "Session ticket " << Botan::hex_encode(session.session_ticket()) << std::endl; + } + + message_received = false; + message_sent = false; + + return true; + } + + void tls_record_received(uint64_t, const uint8_t input[], size_t input_len) override { + for (size_t i = 0; i != input_len; ++i) { + const char c = static_cast(input[i]); + m_line_buf += c; + if (c == '\n') { + m_pending_output.push_back(m_line_buf); + std::cout << m_line_buf << std::endl; + m_line_buf.clear(); + } + } + } + + void tls_emit_data(const uint8_t buf[], size_t length) override { + ssize_t sent = ::send(m_socket, buf, static_cast(length), MSG_NOSIGNAL); + + if (sent == -1) { + std::cout << "Error writing to socket - " << std::strerror(errno) << std::endl; + } else if (sent != static_cast(length)) { + std::cout << "Packet of length " << length << " truncated to " << sent << std::endl; + } + } + + void tls_alert(Botan::TLS::Alert alert) override { + std::cout << "Alert: " << alert.type_string() << std::endl; + } + + std::string tls_server_choose_app_protocol(const std::vector &client_protos) override { + for (unsigned int i = 0; i < client_protos.size(); i++) { + if (client_protos[i].compare(alpn[0]) == 0) { + return client_protos[i]; + } + } + throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::NO_APPLICATION_PROTOCOL, "INVALID ALPN"); + } + + /*void tls_verify_cert_chain( + const std::vector &cert_chain, + const std::vector> &ocsp_responses, + const std::vector &trusted_roots, + Botan::Usage_Type usage, + const std::string &hostname, + const Botan::TLS::Policy &policy) override + { + std::cout << hostname << "HOSTNAMEASDASDS" << std::endl; + if (cert_chain.empty()) + throw Botan::Invalid_Argument("Certificate chain was empty"); + + Botan::Path_Validation_Restrictions restrictions(policy.require_cert_revocation_info(), + policy.minimum_signature_strength()); + + Botan::Path_Validation_Result result = + x509_path_validate(cert_chain, + restrictions, + trusted_roots, + (usage == Botan::Usage_Type::TLS_SERVER_AUTH ? hostname : ""), + usage, + std::chrono::system_clock::now(), + tls_verify_cert_chain_ocsp_timeout(), + ocsp_responses); + + if (!result.successful_validation()) + { + throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::BAD_CERTIFICATE, + "Certificate validation failure: " + result.result_string()); + } + } */ +}; + +int main(int argc, char **argv) { + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:c:k:")) != -1) { + switch (opt) { + case 'a': + alpn[0] = optarg; + break; + case 's': + servername = optarg; + break; + case 'c': + cert = optarg; + break; + case 'k': + key = optarg; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-c certfile] [-k keyfile] \n", + argv[0]); + exit(EXIT_FAILURE); + } + } + std::cout << "Parameters alpn=" << alpn[0] << " servername=" << servername << " cert=" << cert << " key=" << key << std::endl; + + Basic_TLS_Policy policy; + + Botan::AutoSeeded_RNG rng; + Callbacks callbacks; + Botan::TLS::Session_Manager_In_Memory session_manager(rng); + + Basic_Credentials_Manager creds(cert, key); + + //std::cout << "Listening for new connections on " << transport << " port " << port << std::endl; + + socket_type server_fd = make_server_socket(port); + + while (true) { + m_socket = ::accept(server_fd, nullptr, nullptr); + + Botan::TLS::Server server( + callbacks, + session_manager, + creds, + policy, + rng, + false); + + try { + while (!server.is_closed()) { + try { + uint8_t buf[4 * 1024] = {0}; + ssize_t got = ::recv(m_socket, Botan::cast_uint8_ptr_to_char(buf), sizeof(buf), 0); + + if (got == -1) { + std::cerr << "Error in socket read - " << std::strerror(errno) << std::endl; + break; + } + + if (got == 0) { + std::cerr << "EOF on socket" << std::endl; + break; + } + + server.received_data(buf, got); + + while (server.is_active() && !m_pending_output.empty()) { + std::string output = m_pending_output.front(); + m_pending_output.pop_front(); + //server.send(output); + + if (!message_sent) { + std::string message = "Hello from Server!"; + server.send(message); + message_sent = true; + } + + if (output == "quit\n") { + server.close(); + } + } + + } catch (std::exception &e) { + std::cerr << "Connection problem: " << e.what() << std::endl; + ::close(m_socket); + m_socket = -1; + } + } + } catch (Botan::Exception &e) { + std::cerr << "Connection failed: " << e.what() << "\n"; + } + + ::close(m_socket); + m_socket = -1; + } + ::close(server_fd); +} \ No newline at end of file diff --git a/evaluation-libraries/botan/server/server.h b/evaluation-libraries/botan/server/server.h new file mode 100644 index 0000000..8397d37 --- /dev/null +++ b/evaluation-libraries/botan/server/server.h @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef int socket_type; +typedef size_t sendrecv_len_type; + +socket_type make_server_socket(uint16_t port) { + socket_type fd = ::socket(PF_INET, SOCK_STREAM, 0); + if (fd == -1) { + std::cerr << "Unable to acquire socket" << std::endl; + } + + sockaddr_in socket_info; + Botan::clear_mem(&socket_info, 1); + socket_info.sin_family = AF_INET; + socket_info.sin_port = htons(port); + + // FIXME: support limiting listeners + socket_info.sin_addr.s_addr = INADDR_ANY; + + if (::bind(fd, reinterpret_cast(&socket_info), sizeof(struct sockaddr)) != 0) { + ::close(fd); + std::cerr << "server bind failed" << std::endl; + } + + if (::listen(fd, 100) != 0) { + ::close(fd); + std::cerr << "listen failed" << std::endl; + } + return fd; +} + +/* class Basic_TLS_Policy final : public Botan::TLS::Policy +{ +public: + bool require_cert_revocation_info() const override + { + return false; + } + std::vector allowed_ciphers() const override + { + return {"ChaCha20Poly1305", "AES-256/GCM", "AES-128/GCM"}; + } + std::vector allowed_signature_hashes() const override + { + return {"SHA-512", "SHA-384"}; + } + std::vector allowed_macs() const override + { + return {"AEAD"}; + } + std::vector allowed_key_exchange_methods() const override + { + return {"CECPQ1", "ECDH"}; + } + bool allow_tls10() const override { return false; } + bool allow_tls11() const override { return false; } + bool allow_tls12() const override { return true; } +}; */ diff --git a/evaluation-libraries/build-everything.sh b/evaluation-libraries/build-everything.sh new file mode 100755 index 0000000..dd0dfc8 --- /dev/null +++ b/evaluation-libraries/build-everything.sh @@ -0,0 +1,5 @@ +#!/bin/bash +for library in baseimage openssl bearssl botan java gnutls golang mbedtls wolfssl rustls; do + (cd "$library" + ./build.sh); +done diff --git a/evaluation-libraries/gnutls/CMakeLists.txt b/evaluation-libraries/gnutls/CMakeLists.txt new file mode 100644 index 0000000..3ce00f0 --- /dev/null +++ b/evaluation-libraries/gnutls/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-gnutls VERSION 0.1.0) + + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +include(CMakeToolsHelpers OPTIONAL) +include(FeatureSummary) + +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) +include(CheckCXXSourceCompiles) + + + +add_subdirectory(client) +add_subdirectory(server) diff --git a/evaluation-libraries/gnutls/Dockerfile b/evaluation-libraries/gnutls/Dockerfile new file mode 100644 index 0000000..5a4aa36 --- /dev/null +++ b/evaluation-libraries/gnutls/Dockerfile @@ -0,0 +1,38 @@ +# syntax=docker/dockerfile:1 +FROM tls-baseimage as tls-gnutls +ARG VERSION=3.7.2 +# RUN git clone --depth=1 -b ${VERSION} https://gitlab.com/gnutls/gnutls.git +# RUN apk add guile-dev +# ENV PKG_CONFIG_PATH=/build/lib/pkgconfig/ +# # RUN apk add flex gmp-dev libunistring-dev libffi-dev gc-dev +# # RUN git clone https://github.com/cky/guile.git +# # WORKDIR /src/guile +# # RUN ./autogen.sh +# # RUN ./configure +# # RUN make + +# RUN wget https://ftp.gnu.org/gnu/autogen/rel5.18.12/autogen-5.18.12.tar.gz +# RUN tar -xzf autogen-5.18.12.tar.gz +# WORKDIR /src/autogen-5.18.12/ +# RUN ./configure +# RUN make && make install + +# WORKDIR /src/gnutls +# RUN git submodule update --init --no-fetch +# RUN ./bootstrap +# RUN ./configure --with-included-libtasn1 --with-included-unistring --disable-maintainer-mode --disable-doc --disable-full-test-suite --disable-cxx --disable-padlock --without-p11-kit --without-tpm +# RUN make + +RUN apk add --no-cache gnutls-dev + +ADD cmake /build/cmake +ADD server /build/server +ADD client /build/client +ADD CMakeLists.txt /build/CMakeLists.txt +WORKDIR /build +RUN cmake . .. && make +RUN mv /build/server/server / +RUN mv /build/client/client / +COPY --from=tls-openssl /openssl-client /openssl-client +WORKDIR / +CMD ["/server"] diff --git a/evaluation-libraries/gnutls/LICENSE b/evaluation-libraries/gnutls/LICENSE new file mode 100644 index 0000000..efe80f5 --- /dev/null +++ b/evaluation-libraries/gnutls/LICENSE @@ -0,0 +1,23 @@ +LICENSING +========= + +Since GnuTLS version 3.1.10, the core library is released under +the GNU Lesser General Public License (LGPL) version 2.1 or later +(see doc/COPYING.LESSER for the license terms). + +The GNU LGPL applies to the main GnuTLS library, while the +included applications as well as gnutls-openssl +library are under the GNU GPL version 3. The gnutls library is +located in the lib/ and libdane/ directories, while the applications +in src/ and, the gnutls-openssl library is at extra/. + +The documentation in doc/ is under the GNU FDL license 1.3. + + +Note, however, that the nettle and the gmp libraries which are +GnuTLS dependencies, they are distributed under a LGPLv3+ or GPLv2+ dual +license. As such binaries linking to them need to adhere to either LGPLv3+ +or the GPLv2+ license. + +For any copyright year range specified as YYYY-ZZZZ in this package +note that the range specifies every single year in that closed interval. \ No newline at end of file diff --git a/evaluation-libraries/gnutls/build.sh b/evaluation-libraries/gnutls/build.sh new file mode 100755 index 0000000..3b4ad38 --- /dev/null +++ b/evaluation-libraries/gnutls/build.sh @@ -0,0 +1 @@ +docker build . -t tls-gnutls diff --git a/evaluation-libraries/gnutls/client/CMakeLists.txt b/evaluation-libraries/gnutls/client/CMakeLists.txt new file mode 100644 index 0000000..6adf40d --- /dev/null +++ b/evaluation-libraries/gnutls/client/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-gnutls-client VERSION 0.1.0) + +find_package(GnuTLS REQUIRED) +include_directories(SYSTEM ${GNUTLS_INCLUDE_DIR}) + +add_executable(client client.c) +target_link_libraries(client ${GNUTLS_LIBRARIES}) +target_compile_options(client PRIVATE -Wall -Wextra) \ No newline at end of file diff --git a/evaluation-libraries/gnutls/client/client.c b/evaluation-libraries/gnutls/client/client.c new file mode 100644 index 0000000..89516e6 --- /dev/null +++ b/evaluation-libraries/gnutls/client/client.c @@ -0,0 +1,182 @@ +/* This example code is placed in the public domain. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "examples.h" +#include "tcp.c" + +/* A very basic TLS client, with X.509 authentication and server certificate + * verification. Note that error recovery is minimal for simplicity. + */ + +#define CHECK(x) assert((x) >= 0) +#define LOOP_CHECK(rval, cmd) \ + do { \ + rval = cmd; \ + } while (rval == GNUTLS_E_AGAIN || rval == GNUTLS_E_INTERRUPTED); \ + assert(rval >= 0) + +#define MAX_BUF 1024 +#define MSG "Hello from Client!" + +const char *host = "localhost"; +const char *port = "4433"; +const char *servername = "tls-server.com"; +const char *cert = "/etc/ssl/certs/ca.crt"; +gnutls_datum_t alpn = {.data = (unsigned char *)"http/1.1", .size = 8U}; + +int main(int argc, char *argv[]) { + // Disable buffering on stdout so docker output is shown + setbuf(stdout, NULL); + + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:c:h:p")) != -1) { + switch (opt) { + case 'a': + alpn.data = (unsigned char *)optarg; + alpn.size = strlen(optarg); + break; + case 's': + servername = optarg; + break; + case 'h': + host = optarg; + break; + case 'p': + port = optarg; + break; + case 'c': + cert = optarg; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-h ip] [-p port] [-c certificate] \n", + argv[0]); + exit(EXIT_FAILURE); + } + } + //std::cout << "Parameters alpn=" << alpn.data << " servername=" << servername << " cert=" << cert << " host=" << host << std::endl; + printf("Parameters alpn=%s servername=%s cert=%s host=%s port=%s \n", alpn.data, servername, cert, host, port); + + int ret, socket, ii; + gnutls_session_t session; + char buffer[MAX_BUF + 1], *desc; + gnutls_datum_t out; + gnutls_certificate_type_t type; + unsigned status; + gnutls_certificate_credentials_t xcred; + + if (gnutls_check_version("3.4.6") == NULL) { + fprintf(stderr, "GnuTLS 3.4.6 or later is required for this example\n"); + exit(1); + } + + /* for backwards compatibility with gnutls < 3.3.0 */ + CHECK(gnutls_global_init()); + + /* X509 stuff */ + CHECK(gnutls_certificate_allocate_credentials(&xcred)); + CHECK(gnutls_certificate_set_x509_trust_file(xcred, cert, GNUTLS_X509_FMT_PEM)); + + /* sets the system trusted CAs for Internet PKI */ + //CHECK(gnutls_certificate_set_x509_system_trust(xcred)); + + /* If client holds a certificate it can be set using the following: + * + gnutls_certificate_set_x509_key_file (xcred, "cert.pem", "key.pem", + GNUTLS_X509_FMT_PEM); + */ + + /* Initialize TLS session */ + CHECK(gnutls_init(&session, GNUTLS_CLIENT)); + + // Set SNI + CHECK(gnutls_server_name_set(session, GNUTLS_NAME_DNS, servername, strlen(servername))); + + // Set strict ALPN + CHECK(gnutls_alpn_set_protocols(session, &alpn, 1, GNUTLS_ALPN_MANDATORY)); + + /* It is recommended to use the default priorities */ + CHECK(gnutls_set_default_priority(session)); + + /* put the x509 credentials to the current session */ + CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred)); + + // hostname verification + gnutls_session_set_verify_cert(session, servername, 0); + + // connect to the peer + socket = tcp_connect(host, port); + + gnutls_transport_set_int(session, socket); + gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + // do the handshake + do { + ret = gnutls_handshake(session); + } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + if (ret < 0) { + if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) { + /* check certificate verification status */ + type = gnutls_certificate_type_get(session); + status = gnutls_session_get_verify_cert_status(session); + CHECK(gnutls_certificate_verification_status_print(status, type, &out, 0)); + printf("cert verify output: %s\n", out.data); + gnutls_free(out.data); + } + fprintf(stderr, "*** Handshake failed: %s\n", gnutls_strerror(ret)); + goto end; + } else { + desc = gnutls_session_get_desc(session); + //printf("- Session info: %s\n", desc); + gnutls_free(desc); + } + + // Send message to server + LOOP_CHECK(ret, gnutls_record_send(session, MSG, strlen(MSG))); + + // Receive message from server + LOOP_CHECK(ret, gnutls_record_recv(session, buffer, MAX_BUF)); + if (ret == 0) { + printf("- Peer has closed the TLS connection\n"); + goto end; + } else if (ret < 0 && gnutls_error_is_fatal(ret) == 0) { + fprintf(stderr, "*** Warning: %s\n", gnutls_strerror(ret)); + } else if (ret < 0) { + fprintf(stderr, "*** Error: %s\n", gnutls_strerror(ret)); + goto end; + } + + if (ret > 0) { + //printf("- Received %d bytes: ", ret); + //std::cout << ret << std::endl; + for (ii = 0; ii < ret; ii++) { + fputc(buffer[ii], stdout); + } + fputs("\n", stdout); + ret = 0; + } + + CHECK(gnutls_bye(session, GNUTLS_SHUT_RDWR)); + +end: + + tcp_close(socket); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(xcred); + + gnutls_global_deinit(); + + return ret; +} diff --git a/evaluation-libraries/gnutls/client/examples.h b/evaluation-libraries/gnutls/client/examples.h new file mode 100644 index 0000000..0c451b3 --- /dev/null +++ b/evaluation-libraries/gnutls/client/examples.h @@ -0,0 +1,26 @@ +/* This example code is placed in the public domain. */ +// gnutls/gnutls/doc/examples + +#ifndef EXAMPLES_H +#define EXAMPLES_H + +void check_alert(gnutls_session_t session, int ret); + +int write_pkcs12(const gnutls_datum_t *cert, + const gnutls_datum_t *pkcs8_key, const char *password); + +void verify_certificate(gnutls_session_t session, const char *hostname); + +int print_info(gnutls_session_t session); + +void print_x509_certificate_info(gnutls_session_t session); + +int _ssh_verify_certificate_callback(gnutls_session_t session); + +void verify_certificate_chain(const char *hostname, + const gnutls_datum_t *cert_chain, + int cert_chain_length); + +int verify_certificate_callback(gnutls_session_t session); + +#endif /* EXAMPLES_H */ diff --git a/evaluation-libraries/gnutls/client/tcp.c b/evaluation-libraries/gnutls/client/tcp.c new file mode 100644 index 0000000..07857c6 --- /dev/null +++ b/evaluation-libraries/gnutls/client/tcp.c @@ -0,0 +1,82 @@ +/* This example code is placed in the public domain. */ +// gnutls/gnutls/doc/examples + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* tcp.c */ +void tcp_close(int sd); + +/* Connects to the peer and returns a socket + * descriptor. + */ +int tcp_connect(const char *hostname, const char *port) { + int sockfd, portno; + struct sockaddr_in serv_addr; + struct hostent *server; + portno = atoi(port); + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) + printf("ERROR opening socket"); + server = gethostbyname(hostname); + if (server == NULL) { + fprintf(stderr, "ERROR, no such host\n"); + exit(0); + } + bzero((char *)&serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, + (char *)&serv_addr.sin_addr.s_addr, + server->h_length); + serv_addr.sin_port = htons(portno); + int err = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + if (err < 0) { + fprintf(stderr, "Connect error\n"); + exit(1); + } + return sockfd; + /*int err, sd; + struct sockaddr_in sa; + + struct hostent *host = gethostbyname(hostname); + if (!host) + { + printf("unable to resolve : %s\n", hostname); + return false; + } + + //printf("%s", host->h_addr_list[0]); + + sd = socket(AF_INET, SOCK_STREAM, 0); + + memset(&sa, '\0', sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(atoi(port)); + inet_pton(AF_INET, host->h_addr_list[0], &sa.sin_addr); + + err = connect(sd, (struct sockaddr *) &sa, sizeof(sa)); + if (err < 0) { + fprintf(stderr, "Connect error\n"); + exit(1); + } + + return sd;*/ +} + +/* closes the given socket descriptor. + */ +extern void tcp_close(int sd) { + shutdown(sd, SHUT_RDWR); /* no more receptions */ + close(sd); +} diff --git a/evaluation-libraries/gnutls/cmake/FindGnuTLS.cmake b/evaluation-libraries/gnutls/cmake/FindGnuTLS.cmake new file mode 100644 index 0000000..5a059b7 --- /dev/null +++ b/evaluation-libraries/gnutls/cmake/FindGnuTLS.cmake @@ -0,0 +1,84 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindGnuTLS +---------- + +Find the GNU Transport Layer Security library (gnutls) + +IMPORTED Targets +^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.16 + +This module defines :prop_tgt:`IMPORTED` target ``GnuTLS::GnuTLS``, if +gnutls has been found. + +Result Variables +^^^^^^^^^^^^^^^^ + +``GNUTLS_FOUND`` + System has gnutls +``GNUTLS_INCLUDE_DIR`` + The gnutls include directory +``GNUTLS_LIBRARIES`` + The libraries needed to use gnutls +``GNUTLS_DEFINITIONS`` + Compiler switches required for using gnutls +``GNUTLS_VERSION`` + version of gnutls. +#]=======================================================================] + +# Note that this doesn't try to find the gnutls-extra package. + + +if (GNUTLS_INCLUDE_DIR AND GNUTLS_LIBRARY) + # in cache already + set(gnutls_FIND_QUIETLY TRUE) +endif () + +if (NOT WIN32) + # try using pkg-config to get the directories and then use these values + # in the find_path() and find_library() calls + # also fills in GNUTLS_DEFINITIONS, although that isn't normally useful + find_package(PkgConfig QUIET) + PKG_CHECK_MODULES(PC_GNUTLS QUIET gnutls) + set(GNUTLS_DEFINITIONS ${PC_GNUTLS_CFLAGS_OTHER}) + set(GNUTLS_VERSION ${PC_GNUTLS_VERSION}) + # keep for backward compatibility + set(GNUTLS_VERSION_STRING ${PC_GNUTLS_VERSION}) +endif () + +find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h + HINTS + ${PC_GNUTLS_INCLUDEDIR} + ${PC_GNUTLS_INCLUDE_DIRS} + ) + +find_library(GNUTLS_LIBRARY NAMES gnutls libgnutls + HINTS + ${PC_GNUTLS_LIBDIR} + ${PC_GNUTLS_LIBRARY_DIRS} + ) + +mark_as_advanced(GNUTLS_INCLUDE_DIR GNUTLS_LIBRARY) + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GnuTLS + REQUIRED_VARS GNUTLS_LIBRARY GNUTLS_INCLUDE_DIR + VERSION_VAR GNUTLS_VERSION_STRING) + +if(GNUTLS_FOUND) + set(GNUTLS_LIBRARIES ${GNUTLS_LIBRARY}) + set(GNUTLS_INCLUDE_DIRS ${GNUTLS_INCLUDE_DIR}) + + if(NOT TARGET GnuTLS::GnuTLS) + add_library(GnuTLS::GnuTLS UNKNOWN IMPORTED) + set_target_properties(GnuTLS::GnuTLS PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${GNUTLS_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${GNUTLS_DEFINITIONS}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${GNUTLS_LIBRARIES}") + endif() +endif() \ No newline at end of file diff --git a/evaluation-libraries/gnutls/cmake/FindPackageHandleStandardArgs.cmake b/evaluation-libraries/gnutls/cmake/FindPackageHandleStandardArgs.cmake new file mode 100644 index 0000000..ecfad0c --- /dev/null +++ b/evaluation-libraries/gnutls/cmake/FindPackageHandleStandardArgs.cmake @@ -0,0 +1,605 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPackageHandleStandardArgs +----------------------------- + +This module provides functions intended to be used in :ref:`Find Modules` +implementing :command:`find_package()` calls. + +.. command:: find_package_handle_standard_args + + This command handles the ``REQUIRED``, ``QUIET`` and version-related + arguments of :command:`find_package`. It also sets the + ``_FOUND`` variable. The package is considered found if all + variables listed contain valid results, e.g. valid filepaths. + + There are two signatures: + + .. code-block:: cmake + + find_package_handle_standard_args( + (DEFAULT_MSG|) + ... + ) + + find_package_handle_standard_args( + [FOUND_VAR ] + [REQUIRED_VARS ...] + [VERSION_VAR ] + [HANDLE_VERSION_RANGE] + [HANDLE_COMPONENTS] + [CONFIG_MODE] + [NAME_MISMATCHED] + [REASON_FAILURE_MESSAGE ] + [FAIL_MESSAGE ] + ) + + The ``_FOUND`` variable will be set to ``TRUE`` if all + the variables ``...`` are valid and any optional + constraints are satisfied, and ``FALSE`` otherwise. A success or + failure message may be displayed based on the results and on + whether the ``REQUIRED`` and/or ``QUIET`` option was given to + the :command:`find_package` call. + + The options are: + + ``(DEFAULT_MSG|)`` + In the simple signature this specifies the failure message. + Use ``DEFAULT_MSG`` to ask for a default message to be computed + (recommended). Not valid in the full signature. + + ``FOUND_VAR `` + .. deprecated:: 3.3 + + Specifies either ``_FOUND`` or + ``_FOUND`` as the result variable. This exists only + for compatibility with older versions of CMake and is now ignored. + Result variables of both names are always set for compatibility. + + ``REQUIRED_VARS ...`` + Specify the variables which are required for this package. + These may be named in the generated failure message asking the + user to set the missing variable values. Therefore these should + typically be cache entries such as ``FOO_LIBRARY`` and not output + variables like ``FOO_LIBRARIES``. + + .. versionchanged:: 3.18 + If ``HANDLE_COMPONENTS`` is specified, this option can be omitted. + + ``VERSION_VAR `` + Specify the name of a variable that holds the version of the package + that has been found. This version will be checked against the + (potentially) specified required version given to the + :command:`find_package` call, including its ``EXACT`` option. + The default messages include information about the required + version and the version which has been actually found, both + if the version is ok or not. + + ``HANDLE_VERSION_RANGE`` + .. versionadded:: 3.19 + + Enable handling of a version range, if one is specified. Without this + option, a developer warning will be displayed if a version range is + specified. + + ``HANDLE_COMPONENTS`` + Enable handling of package components. In this case, the command + will report which components have been found and which are missing, + and the ``_FOUND`` variable will be set to ``FALSE`` + if any of the required components (i.e. not the ones listed after + the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are + missing. + + ``CONFIG_MODE`` + Specify that the calling find module is a wrapper around a + call to ``find_package( NO_MODULE)``. This implies + a ``VERSION_VAR`` value of ``_VERSION``. The command + will automatically check whether the package configuration file + was found. + + ``REASON_FAILURE_MESSAGE `` + .. versionadded:: 3.16 + + Specify a custom message of the reason for the failure which will be + appended to the default generated message. + + ``FAIL_MESSAGE `` + Specify a custom failure message instead of using the default + generated message. Not recommended. + + ``NAME_MISMATCHED`` + .. versionadded:: 3.17 + + Indicate that the ```` does not match + ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a + warning, but it may be intentional for usage of the command for components + of a larger package. + +Example for the simple signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibXml2 DEFAULT_MSG + LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) + +The ``LibXml2`` package is considered to be found if both +``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. +Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found +and ``REQUIRED`` was used, it fails with a +:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was +used or not. If it is found, success will be reported, including +the content of the first ````. On repeated CMake runs, +the same message will not be printed again. + +.. note:: + + If ```` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the + calling module, a warning that there is a mismatch is given. The + ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using + the old signature and the ``NAME_MISMATCHED`` argument using the new + signature. To avoid forcing the caller to require newer versions of CMake for + usage, the variable's value will be used if defined when the + ``NAME_MISMATCHED`` argument is not passed for the new signature (but using + both is an error).. + +Example for the full signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibArchive + REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR + VERSION_VAR LibArchive_VERSION) + +In this case, the ``LibArchive`` package is considered to be found if +both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. +Also the version of ``LibArchive`` will be checked by using the version +contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, +the default messages will be printed. + +Another example for the full signature: + +.. code-block:: cmake + + find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) + find_package_handle_standard_args(Automoc4 CONFIG_MODE) + +In this case, a ``FindAutmoc4.cmake`` module wraps a call to +``find_package(Automoc4 NO_MODULE)`` and adds an additional search +directory for ``automoc4``. Then the call to +``find_package_handle_standard_args`` produces a proper success/failure +message. + +.. command:: find_package_check_version + + .. versionadded:: 3.19 + + Helper function which can be used to check if a ```` is valid + against version-related arguments of :command:`find_package`. + + .. code-block:: cmake + + find_package_check_version( + [HANDLE_VERSION_RANGE] + [RESULT_MESSAGE_VARIABLE ] + ) + + The ```` will hold a boolean value giving the result of the check. + + The options are: + + ``HANDLE_VERSION_RANGE`` + Enable handling of a version range, if one is specified. Without this + option, a developer warning will be displayed if a version range is + specified. + + ``RESULT_MESSAGE_VARIABLE `` + Specify a variable to get back a message describing the result of the check. + +Example for the usage: + +.. code-block:: cmake + + find_package_check_version(1.2.3 result HANDLE_VERSION_RANGE + RESULT_MESSAGE_VARIABLE reason) + if (result) + message (STATUS "${reason}") + else() + message (FATAL_ERROR "${reason}") + endif() +#]=======================================================================] + +include(${CMAKE_SOURCE_DIR}/cmake/FindPackageMessage.cmake) + + +cmake_policy(PUSH) +# numbers and boolean constants +cmake_policy (SET CMP0012 NEW) +# IN_LIST operator +cmake_policy (SET CMP0057 NEW) + + +# internal helper macro +macro(_FPHSA_FAILURE_MESSAGE _msg) + set (__msg "${_msg}") + if (FPHSA_REASON_FAILURE_MESSAGE) + string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") + endif() + if (${_NAME}_FIND_REQUIRED) + message(FATAL_ERROR "${__msg}") + else () + if (NOT ${_NAME}_FIND_QUIETLY) + message(STATUS "${__msg}") + endif () + endif () +endmacro() + + +# internal helper macro to generate the failure message when used in CONFIG_MODE: +macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) + # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: + if(${_NAME}_CONFIG) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") + else() + # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. + # List them all in the error message: + if(${_NAME}_CONSIDERED_CONFIGS) + set(configsText "") + list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) + math(EXPR configsCount "${configsCount} - 1") + foreach(currentConfigIndex RANGE ${configsCount}) + list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) + list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) + string(APPEND configsText "\n ${filename} (version ${version})") + endforeach() + if (${_NAME}_NOT_FOUND_MESSAGE) + if (FPHSA_REASON_FAILURE_MESSAGE) + string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ") + else() + set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}") + endif() + else() + string(APPEND configsText "\n") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}") + + else() + # Simple case: No Config-file was found at all: + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") + endif() + endif() +endmacro() + + +function(FIND_PACKAGE_CHECK_VERSION version result) + cmake_parse_arguments (PARSE_ARGV 2 FPCV "HANDLE_VERSION_RANGE;NO_AUTHOR_WARNING_VERSION_RANGE" "RESULT_MESSAGE_VARIABLE" "") + + if (FPCV_UNPARSED_ARGUMENTS) + message (FATAL_ERROR "find_package_check_version(): ${FPCV_UNPARSED_ARGUMENTS}: unexpected arguments") + endif() + if ("RESULT_MESSAGE_VARIABLE" IN_LIST FPCV_KEYWORDS_MISSING_VALUES) + message (FATAL_ERROR "find_package_check_version(): RESULT_MESSAGE_VARIABLE expects an argument") + endif() + + set (${result} FALSE PARENT_SCOPE) + if (FPCV_RESULT_MESSAGE_VARIABLE) + unset (${FPCV_RESULT_MESSAGE_VARIABLE} PARENT_SCOPE) + endif() + + if (_CMAKE_FPHSA_PACKAGE_NAME) + set (package "${_CMAKE_FPHSA_PACKAGE_NAME}") + elseif (CMAKE_FIND_PACKAGE_NAME) + set (package "${CMAKE_FIND_PACKAGE_NAME}") + else() + message (FATAL_ERROR "find_package_check_version(): Cannot be used outside a 'Find Module'") + endif() + + if (NOT FPCV_NO_AUTHOR_WARNING_VERSION_RANGE + AND ${package}_FIND_VERSION_RANGE AND NOT FPCV_HANDLE_VERSION_RANGE) + message(AUTHOR_WARNING + "`find_package()` specify a version range but the option " + "HANDLE_VERSION_RANGE` is not passed to `find_package_check_version()`. " + "Only the lower endpoint of the range will be used.") + endif() + + + set (version_ok FALSE) + unset (version_msg) + + if (FPCV_HANDLE_VERSION_RANGE AND ${package}_FIND_VERSION_RANGE) + if ((${package}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" + AND version VERSION_GREATER_EQUAL ${package}_FIND_VERSION_MIN) + AND ((${package}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" + AND version VERSION_LESS_EQUAL ${package}_FIND_VERSION_MAX) + OR (${package}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" + AND version VERSION_LESS ${package}_FIND_VERSION_MAX))) + set (version_ok TRUE) + set(version_msg "(found suitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\")") + else() + set(version_msg "Found unsuitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\"") + endif() + elseif (DEFINED ${package}_FIND_VERSION) + if(${package}_FIND_VERSION_EXACT) # exact version required + # count the dots in the version string + string(REGEX REPLACE "[^.]" "" version_dots "${version}") + # add one dot because there is one dot more than there are components + string(LENGTH "${version_dots}." version_dots) + if (version_dots GREATER ${package}_FIND_VERSION_COUNT) + # Because of the C++ implementation of find_package() ${package}_FIND_VERSION_COUNT + # is at most 4 here. Therefore a simple lookup table is used. + if (${package}_FIND_VERSION_COUNT EQUAL 1) + set(version_regex "[^.]*") + elseif (${package}_FIND_VERSION_COUNT EQUAL 2) + set(version_regex "[^.]*\\.[^.]*") + elseif (${package}_FIND_VERSION_COUNT EQUAL 3) + set(version_regex "[^.]*\\.[^.]*\\.[^.]*") + else() + set(version_regex "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") + endif() + string(REGEX REPLACE "^(${version_regex})\\..*" "\\1" version_head "${version}") + if (NOT ${package}_FIND_VERSION VERSION_EQUAL version_head) + set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") + else () + set(version_ok TRUE) + set(version_msg "(found suitable exact version \"${_FOUND_VERSION}\")") + endif () + else () + if (NOT ${package}_FIND_VERSION VERSION_EQUAL version) + set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") + else () + set(version_ok TRUE) + set(version_msg "(found suitable exact version \"${version}\")") + endif () + endif () + else() # minimum version + if (${package}_FIND_VERSION VERSION_GREATER version) + set(version_msg "Found unsuitable version \"${version}\", but required is at least \"${${package}_FIND_VERSION}\"") + else() + set(version_ok TRUE) + set(version_msg "(found suitable version \"${version}\", minimum required is \"${${package}_FIND_VERSION}\")") + endif() + endif() + else () + set(version_ok TRUE) + set(version_msg "(found version \"${version}\")") + endif() + + set (${result} ${version_ok} PARENT_SCOPE) + if (FPCV_RESULT_MESSAGE_VARIABLE) + set (${FPCV_RESULT_MESSAGE_VARIABLE} "${version_msg}" PARENT_SCOPE) + endif() +endfunction() + + +function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) + + # Set up the arguments for `cmake_parse_arguments`. + set(options CONFIG_MODE HANDLE_COMPONENTS NAME_MISMATCHED HANDLE_VERSION_RANGE) + set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR) + set(multiValueArgs REQUIRED_VARS) + + # Check whether we are in 'simple' or 'extended' mode: + set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) + list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) + + unset(FPHSA_NAME_MISMATCHED_override) + if (DEFINED FPHSA_NAME_MISMATCHED) + # If the variable NAME_MISMATCHED variable is set, error if it is passed as + # an argument. The former is for old signatures, the latter is for new + # signatures. + list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx) + if (NOT name_mismatched_idx EQUAL "-1") + message(FATAL_ERROR + "The `NAME_MISMATCHED` argument may only be specified by the argument or " + "the variable, not both.") + endif () + + # But use the variable if it is not an argument to avoid forcing minimum + # CMake version bumps for calling modules. + set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}") + endif () + + if(${INDEX} EQUAL -1) + set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) + set(FPHSA_REQUIRED_VARS ${ARGN}) + set(FPHSA_VERSION_VAR) + else() + cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) + + if(FPHSA_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") + endif() + + if(NOT FPHSA_FAIL_MESSAGE) + set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") + endif() + + # In config-mode, we rely on the variable _CONFIG, which is set by find_package() + # when it successfully found the config-file, including version checking: + if(FPHSA_CONFIG_MODE) + list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) + list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) + set(FPHSA_VERSION_VAR ${_NAME}_VERSION) + endif() + + if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS) + message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") + endif() + endif() + + if (DEFINED FPHSA_NAME_MISMATCHED_override) + set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}") + endif () + + if (DEFINED CMAKE_FIND_PACKAGE_NAME + AND NOT FPHSA_NAME_MISMATCHED + AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME) + message(AUTHOR_WARNING + "The package name passed to `find_package_handle_standard_args` " + "(${_NAME}) does not match the name of the calling package " + "(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling " + "code that expects `find_package` result variables (e.g., `_FOUND`) " + "to follow a certain pattern.") + endif () + + if (${_NAME}_FIND_VERSION_RANGE AND NOT FPHSA_HANDLE_VERSION_RANGE) + message(AUTHOR_WARNING + "`find_package()` specify a version range but the module ${_NAME} does " + "not support this capability. Only the lower endpoint of the range " + "will be used.") + endif() + + # to propagate package name to FIND_PACKAGE_CHECK_VERSION + set(_CMAKE_FPHSA_PACKAGE_NAME "${_NAME}") + + # now that we collected all arguments, process them + + if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") + set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") + endif() + + if (FPHSA_REQUIRED_VARS) + list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) + endif() + + string(TOUPPER ${_NAME} _NAME_UPPER) + string(TOLOWER ${_NAME} _NAME_LOWER) + + if(FPHSA_FOUND_VAR) + set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND) + set(_FOUND_VAR_MIXED ${_NAME}_FOUND) + if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED OR FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER) + set(_FOUND_VAR ${FPHSA_FOUND_VAR}) + else() + message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.") + endif() + else() + set(_FOUND_VAR ${_NAME_UPPER}_FOUND) + endif() + + # collect all variables which were not found, so they can be printed, so the + # user knows better what went wrong (#6375) + set(MISSING_VARS "") + set(DETAILS "") + # check if all passed variables are valid + set(FPHSA_FOUND_${_NAME} TRUE) + foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) + if(NOT ${_CURRENT_VAR}) + set(FPHSA_FOUND_${_NAME} FALSE) + string(APPEND MISSING_VARS " ${_CURRENT_VAR}") + else() + string(APPEND DETAILS "[${${_CURRENT_VAR}}]") + endif() + endforeach() + if(FPHSA_FOUND_${_NAME}) + set(${_NAME}_FOUND TRUE) + set(${_NAME_UPPER}_FOUND TRUE) + else() + set(${_NAME}_FOUND FALSE) + set(${_NAME_UPPER}_FOUND FALSE) + endif() + + # component handling + unset(FOUND_COMPONENTS_MSG) + unset(MISSING_COMPONENTS_MSG) + + if(FPHSA_HANDLE_COMPONENTS) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(${_NAME}_${comp}_FOUND) + + if(NOT DEFINED FOUND_COMPONENTS_MSG) + set(FOUND_COMPONENTS_MSG "found components:") + endif() + string(APPEND FOUND_COMPONENTS_MSG " ${comp}") + + else() + + if(NOT DEFINED MISSING_COMPONENTS_MSG) + set(MISSING_COMPONENTS_MSG "missing components:") + endif() + string(APPEND MISSING_COMPONENTS_MSG " ${comp}") + + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + string(APPEND MISSING_VARS " ${comp}") + endif() + + endif() + endforeach() + set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") + string(APPEND DETAILS "[c${COMPONENT_MSG}]") + endif() + + # version handling: + set(VERSION_MSG "") + set(VERSION_OK TRUE) + + # check with DEFINED here as the requested or found version may be "0" + if (DEFINED ${_NAME}_FIND_VERSION) + if(DEFINED ${FPHSA_VERSION_VAR}) + set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) + if (FPHSA_HANDLE_VERSION_RANGE) + set (FPCV_HANDLE_VERSION_RANGE HANDLE_VERSION_RANGE) + else() + set(FPCV_HANDLE_VERSION_RANGE NO_AUTHOR_WARNING_VERSION_RANGE) + endif() + find_package_check_version ("${_FOUND_VERSION}" VERSION_OK RESULT_MESSAGE_VARIABLE VERSION_MSG + ${FPCV_HANDLE_VERSION_RANGE}) + else() + # if the package was not found, but a version was given, add that to the output: + if(${_NAME}_FIND_VERSION_EXACT) + set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") + elseif (FPHSA_HANDLE_VERSION_RANGE AND ${_NAME}_FIND_VERSION_RANGE) + set(VERSION_MSG "(Required is version range \"${${_NAME}_FIND_VERSION_RANGE}\")") + else() + set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") + endif() + endif() + else () + # Check with DEFINED as the found version may be 0. + if(DEFINED ${FPHSA_VERSION_VAR}) + set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") + endif() + endif () + + if(VERSION_OK) + string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") + else() + set(${_NAME}_FOUND FALSE) + endif() + + + # print the result: + if (${_NAME}_FOUND) + FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") + else () + + if(FPHSA_CONFIG_MODE) + _FPHSA_HANDLE_FAILURE_CONFIG_MODE() + else() + if(NOT VERSION_OK) + set(RESULT_MSG) + if (_FIRST_REQUIRED_VAR) + string (APPEND RESULT_MSG "found ${${_FIRST_REQUIRED_VAR}}") + endif() + if (COMPONENT_MSG) + if (RESULT_MSG) + string (APPEND RESULT_MSG ", ") + endif() + string (APPEND RESULT_MSG "${FOUND_COMPONENTS_MSG}") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})") + else() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") + endif() + endif() + + endif () + + set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) + set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) +endfunction() + + +cmake_policy(POP) \ No newline at end of file diff --git a/evaluation-libraries/gnutls/cmake/FindPackageMessage.cmake b/evaluation-libraries/gnutls/cmake/FindPackageMessage.cmake new file mode 100644 index 0000000..e53285b --- /dev/null +++ b/evaluation-libraries/gnutls/cmake/FindPackageMessage.cmake @@ -0,0 +1,48 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPackageMessage +------------------ + +.. code-block:: cmake + + find_package_message( "message for user" "find result details") + +This function is intended to be used in FindXXX.cmake modules files. +It will print a message once for each unique find result. This is +useful for telling the user where a package was found. The first +argument specifies the name (XXX) of the package. The second argument +specifies the message to display. The third argument lists details +about the find result so that if they change the message will be +displayed again. The macro also obeys the QUIET argument to the +find_package command. + +Example: + +.. code-block:: cmake + + if(X11_FOUND) + find_package_message(X11 "Found X11: ${X11_X11_LIB}" + "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") + else() + ... + endif() +#]=======================================================================] + +function(find_package_message pkg msg details) + # Avoid printing a message repeatedly for the same find result. + if(NOT ${pkg}_FIND_QUIETLY) + string(REPLACE "\n" "" details "${details}") + set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) + if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") + # The message has not yet been printed. + message(STATUS "${msg}") + + # Save the find details in the cache to avoid printing the same + # message again. + set("${DETAILS_VAR}" "${details}" + CACHE INTERNAL "Details about finding ${pkg}") + endif() + endif() +endfunction() \ No newline at end of file diff --git a/evaluation-libraries/gnutls/docker-compose.yml b/evaluation-libraries/gnutls/docker-compose.yml new file mode 100644 index 0000000..e9b0ecb --- /dev/null +++ b/evaluation-libraries/gnutls/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + gnutls-server: + image: tls-gnutls + openssl-server-wrong-cn: + image: tls-openssl + command: [ "/openssl-server", "-k", "/etc/ssl/cert-data/wrong-cn.com.key", "-c" , "/etc/ssl/cert-data/wrong-cn.com-chain.crt"] + openssl-malicious-alpn: + image: tls-openssl + command: [ "/openssl-server", "-m"] + gnutls-client: + image: tls-gnutls + command: [ "./client.sh", "/client", "gnutls-server", "openssl-server-wrong-cn", "openssl-malicious-alpn" ,"1"] + depends_on: + - gnutls-server + - openssl-server-wrong-cn + - openssl-malicious-alpn \ No newline at end of file diff --git a/evaluation-libraries/gnutls/readme.md b/evaluation-libraries/gnutls/readme.md new file mode 100644 index 0000000..25ecc69 --- /dev/null +++ b/evaluation-libraries/gnutls/readme.md @@ -0,0 +1,11 @@ +# gnuttls example with strict sni and strict alpn + +Tested with GnuTLS 3.7.1 + +needs tls-baseimage already in docker + +based on https://gitlab.com/gnutls/gnutls/-/tree/3.7.1/doc/examples + +```bash +./run.sh +``` \ No newline at end of file diff --git a/evaluation-libraries/gnutls/run.sh b/evaluation-libraries/gnutls/run.sh new file mode 100755 index 0000000..4c46cce --- /dev/null +++ b/evaluation-libraries/gnutls/run.sh @@ -0,0 +1,3 @@ +./build.sh +docker-compose up --abort-on-container-exit --exit-code-from gnutls-client --remove-orphans + \ No newline at end of file diff --git a/evaluation-libraries/gnutls/server/CMakeLists.txt b/evaluation-libraries/gnutls/server/CMakeLists.txt new file mode 100644 index 0000000..5907cc7 --- /dev/null +++ b/evaluation-libraries/gnutls/server/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-gnutls-server VERSION 0.1.0) + +find_package(GnuTLS REQUIRED) +include_directories(SYSTEM ${GNUTLS_INCLUDE_DIR}) + +add_executable(server server.c) +target_link_libraries(server ${GNUTLS_LIBRARIES}) +target_compile_options(server PRIVATE -Wall -Wextra) \ No newline at end of file diff --git a/evaluation-libraries/gnutls/server/server.c b/evaluation-libraries/gnutls/server/server.c new file mode 100644 index 0000000..7bee4d0 --- /dev/null +++ b/evaluation-libraries/gnutls/server/server.c @@ -0,0 +1,168 @@ +/* +This example code is placed in the public domain. +gnutls/gnutls/doc/examples/ex-serv-x509.c +*/ + +#include "server.h" + +u_int16_t port = 4433; +const char *servername = "tls-server.com"; +const char *cert = "/etc/ssl/cert-data/tls-server.com-chain.crt"; +const char *key = "/etc/ssl/cert-data/tls-server.com.key"; +gnutls_datum_t alpn = {.data = (unsigned char *)"http/1.1", .size = 8U}; + +#define MSG "Hello from Server!" + +static int ext_hook_func(void *ctx, unsigned tls_id, + const unsigned char *data, unsigned size) { + if (tls_id == 0) { /* 0 = server_name extension */ + const unsigned char *servername_received = data + 5; + + if (strcmp(servername, servername_received) != 0) { + fprintf(stderr, "INVALID SNI: %s\n", servername_received); + return GNUTLS_E_UNRECOGNIZED_NAME; + } + else { + printf("SNI: %s\n", servername_received); + } + } + return 0; +} + +static int handshake_hook_func(gnutls_session_t session, unsigned int htype, + unsigned when, unsigned int incoming, const gnutls_datum_t *msg) { + // call hook for parsing the raw TLS extension + return gnutls_ext_raw_parse(NULL, ext_hook_func, msg, GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO); +} + +int main(int argc, char **argv) { + // Disable buffering on stdout so docker output is shown + setbuf(stdout, NULL); + + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:c:k:")) != -1) { + switch (opt) { + case 'a': + alpn.data = (unsigned char *)optarg; + alpn.size = strlen(optarg); + break; + case 's': + servername = optarg; + break; + case 'c': + cert = optarg; + break; + case 'k': + key = optarg; + break; + /*case 'p': + port = optarg; + break;*/ + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-c certfile] [-k keyfile] \n", + argv[0]); + exit(EXIT_FAILURE); + } + } + printf("Parameters alpn=%s servername=%s cert=%s key=%s port=%d \n", alpn.data, servername, cert, key, port); + + int sd, ret; + gnutls_certificate_credentials_t x509_cred; + gnutls_priority_t priority_cache; + struct sockaddr_in sa_cli; + socklen_t client_len; + gnutls_session_t session; + char buffer[MAX_BUF + 1]; + + // Initialize gnutls + CHECK(gnutls_global_init()); + CHECK(gnutls_certificate_allocate_credentials(&x509_cred)); + + // Load certificate and key file + CHECK(gnutls_certificate_set_x509_key_file(x509_cred, cert, key, GNUTLS_X509_FMT_PEM)); + + CHECK(gnutls_priority_init(&priority_cache, NULL, NULL)); + gnutls_certificate_set_known_dh_params(x509_cred, GNUTLS_SEC_PARAM_MEDIUM); + + // Create socket and listen on port + int listen_sd = create_socket(port); + listen(listen_sd, 1024); + client_len = sizeof(sa_cli); + for (;;) { + CHECK(gnutls_init(&session, GNUTLS_SERVER)); + CHECK(gnutls_priority_set(session, priority_cache)); + CHECK(gnutls_priority_set_direct(session, "NORMAL:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:+VERS-TLS1.2", 0)); + CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred)); + + // Set ALPN + CHECK(gnutls_alpn_set_protocols(session, &alpn, 1, GNUTLS_ALPN_MANDATORY)); + + gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_CLIENT_HELLO, GNUTLS_HOOK_PRE, handshake_hook_func); + + /* We don't request any certificate from the client. + * If we did we would need to verify it. One way of + * doing that is shown in the "Verifying a certificate" + * example. + */ + gnutls_certificate_server_set_request(session, GNUTLS_CERT_IGNORE); + gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + sd = accept(listen_sd, (struct sockaddr *)&sa_cli, &client_len); + + gnutls_transport_set_int(session, sd); + + // Handshake + LOOP_CHECK(ret, gnutls_handshake(session)); + if (ret < 0) { + if (ret == GNUTLS_E_NO_APPLICATION_PROTOCOL) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_NO_APPLICATION_PROTOCOL); + } else if (ret == GNUTLS_E_UNRECOGNIZED_NAME) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_UNRECOGNIZED_NAME); + } + + close(sd); + gnutls_deinit(session); + fprintf(stderr, "*** Handshake has failed (%s)\n\n", gnutls_strerror(ret)); + continue; + } + + //for (;;) { + // Receive message from client + LOOP_CHECK(ret, gnutls_record_recv(session, buffer, MAX_BUF)); + + if (ret == 0) { + printf("\n- Peer has closed the GnuTLS connection\n"); + //break; + } else if (ret < 0 && gnutls_error_is_fatal(ret) == 0) { + fprintf(stderr, "*** Warning: %s\n", gnutls_strerror(ret)); + } else if (ret < 0) { + fprintf(stderr, + "\n*** Received corrupted " + "data(%d). Closing the connection.\n\n", + ret); + //break; + } else if (ret > 0) { + printf("%.*s", ret, buffer); + + // Send message to client + CHECK(gnutls_record_send(session, MSG, strlen(MSG))); + LOOP_CHECK(ret, gnutls_bye(session, GNUTLS_SHUT_WR)); + } + //} + printf("\n"); + /* do not wait for the peer to close the connection. */ + //gnutls_bye(session, GNUTLS_SHUT_WR); + + close(sd); + gnutls_deinit(session); + } + close(listen_sd); + + gnutls_certificate_free_credentials(x509_cred); + gnutls_priority_deinit(priority_cache); + + gnutls_global_deinit(); + + return 0; +} diff --git a/evaluation-libraries/gnutls/server/server.h b/evaluation-libraries/gnutls/server/server.h new file mode 100644 index 0000000..85f58b7 --- /dev/null +++ b/evaluation-libraries/gnutls/server/server.h @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHECK(x) assert((x) >= 0) +#define LOOP_CHECK(rval, cmd) \ + do { \ + rval = cmd; \ + } while (rval == GNUTLS_E_AGAIN || rval == GNUTLS_E_INTERRUPTED) + +#define MAX_BUF 1024 + +int create_socket(u_int16_t port) { + int listen_sd; + struct sockaddr_in sa_serv; + int optval = 1; + /* Socket operations + */ + listen_sd = socket(AF_INET, SOCK_STREAM, 0); + + memset(&sa_serv, '\0', sizeof(sa_serv)); + sa_serv.sin_family = AF_INET; + sa_serv.sin_addr.s_addr = INADDR_ANY; + sa_serv.sin_port = htons(port); + + setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int)); + + bind(listen_sd, (struct sockaddr *)&sa_serv, sizeof(sa_serv)); + + return listen_sd; + + printf("Server ready. Listening to port '%d'.\n\n", port); +} \ No newline at end of file diff --git a/evaluation-libraries/golang/Dockerfile b/evaluation-libraries/golang/Dockerfile new file mode 100644 index 0000000..39df3e1 --- /dev/null +++ b/evaluation-libraries/golang/Dockerfile @@ -0,0 +1,14 @@ +# syntax=docker/dockerfile:1 +FROM tls-baseimage +WORKDIR . +ADD server /build/server +ADD client /build/client +WORKDIR /build/server +RUN go build server.go +RUN mv server / +WORKDIR /build/client +RUN go build client.go +RUN mv client / +WORKDIR / +COPY --from=tls-openssl /openssl-client /openssl-client +CMD ["/server"] diff --git a/evaluation-libraries/golang/LICENSE b/evaluation-libraries/golang/LICENSE new file mode 100644 index 0000000..670154e --- /dev/null +++ b/evaluation-libraries/golang/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/evaluation-libraries/golang/README.md b/evaluation-libraries/golang/README.md new file mode 100644 index 0000000..95d1d87 --- /dev/null +++ b/evaluation-libraries/golang/README.md @@ -0,0 +1,12 @@ +# golang tls example with strict sni and strict alpn + +Tested with golang 1.16.7 + +Based on https://github.com/denji/golang-tls + +needs tls-baseimage already in docker + + +```bash +./run.sh +``` \ No newline at end of file diff --git a/evaluation-libraries/golang/build.sh b/evaluation-libraries/golang/build.sh new file mode 100755 index 0000000..6892085 --- /dev/null +++ b/evaluation-libraries/golang/build.sh @@ -0,0 +1 @@ +docker build -t tls-golang . \ No newline at end of file diff --git a/evaluation-libraries/golang/client/client.go b/evaluation-libraries/golang/client/client.go new file mode 100755 index 0000000..27cf7d5 --- /dev/null +++ b/evaluation-libraries/golang/client/client.go @@ -0,0 +1,93 @@ +package main + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "flag" + "io/ioutil" + "log" + "os" + "runtime" + "strings" +) + +var servername string +var certificate string +var host string = "127.0.0.1" +var port string = "4433" +var alpn = []string{""} + +func main() { + log.SetFlags(log.LstdFlags) + + // Get commandline arguments + flag.StringVar(&servername, "s", "tls-server.com", "servername for SNI") + flag.StringVar(&alpn[0], "a", "http/1.1", "ALPN") + flag.StringVar(&certificate, "c", "/etc/ssl/certs/ca.crt", "certicate") + flag.StringVar(&host, "h", "127.0.0.1", "host") + flag.StringVar(&port, "p", "4433", "port") + flag.Parse() + println("Parameters servername=" + servername + " alpn=" + alpn[0] + " cert=" + certificate + " host=" + host + " port=" + port) + + certs := x509.NewCertPool() + + // Read Certificate + pemData, err := ioutil.ReadFile(certificate) + if err != nil { + log.Println(err) + os.Exit(-1) + } + certs.AppendCertsFromPEM(pemData) + + // Setup TLS config + conf := &tls.Config{ + RootCAs: certs, + NextProtos: alpn, + ServerName: servername, + } + if runtime.Version() < "go1.17" { + println("Strict ALPN not implemented in go version. Overriding VerifyConnection") + conf.VerifyConnection = func(cs tls.ConnectionState) error { + if cs.NegotiatedProtocol == "" { + return errors.New("INVALID ALPN") + } else { + log.Println("ALPN:", cs.NegotiatedProtocol) + return nil + } + } + } + + // Connect to host + conn, err := tls.Dial("tcp", host+":"+port, conf) + if err != nil { + log.Println(err) + if strings.Contains(err.Error(), "server selected unadvertised ALPN protocol") { + os.Exit(120) + } else if strings.Contains(err.Error(), "x509: certificate is valid for") { + os.Exit(42) + } + os.Exit(1) + } + + // Send message to server + n, err := conn.Write([]byte("Hello from Client!\n")) + if err != nil { + log.Println(n, err) + os.Exit(2) + return + } + + // Receive message from server + buf := make([]byte, 100) + n, err = conn.Read(buf) + if err != nil { + log.Println(n, err) + os.Exit(3) + return + } + print(string(buf[:n])) + + defer conn.Close() + os.Exit(0) +} diff --git a/evaluation-libraries/golang/client/go.mod b/evaluation-libraries/golang/client/go.mod new file mode 100644 index 0000000..0ef1b48 --- /dev/null +++ b/evaluation-libraries/golang/client/go.mod @@ -0,0 +1,3 @@ +module client + +go 1.17 diff --git a/evaluation-libraries/golang/docker-compose.yml b/evaluation-libraries/golang/docker-compose.yml new file mode 100644 index 0000000..02b031a --- /dev/null +++ b/evaluation-libraries/golang/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3" +networks: + default: + name: tls-network + internal: true +services: + golang-server: + image: tls-golang + openssl-server-wrong-cn: + image: tls-openssl + command: [ "/openssl-server", "-k", "/etc/ssl/cert-data/wrong-cn.com.key", "-c" , "/etc/ssl/cert-data/wrong-cn.com-chain.crt"] + openssl-malicious-alpn: + image: tls-openssl + command: [ "/openssl-server", "-m"] + golang-client: + image: tls-golang + depends_on: + - golang-server + - openssl-server-wrong-cn + - openssl-malicious-alpn + command: [ "/client.sh", "/client", "golang-server", "openssl-server-wrong-cn", "openssl-malicious-alpn" ,"1"] \ No newline at end of file diff --git a/evaluation-libraries/golang/run.sh b/evaluation-libraries/golang/run.sh new file mode 100755 index 0000000..a5e85f0 --- /dev/null +++ b/evaluation-libraries/golang/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --abort-on-container-exit --exit-code-from golang-client --remove-orphans \ No newline at end of file diff --git a/evaluation-libraries/golang/server/go.mod b/evaluation-libraries/golang/server/go.mod new file mode 100644 index 0000000..9a7f23b --- /dev/null +++ b/evaluation-libraries/golang/server/go.mod @@ -0,0 +1,3 @@ +module server + +go 1.17 diff --git a/evaluation-libraries/golang/server/server.go b/evaluation-libraries/golang/server/server.go new file mode 100644 index 0000000..79f12db --- /dev/null +++ b/evaluation-libraries/golang/server/server.go @@ -0,0 +1,112 @@ +package main + +import ( + "bufio" + "crypto/tls" + "errors" + "flag" + "log" + "net" + "runtime" +) + +var servername = "tls-server.com" +var certificate = "certs/tls-server.com-chain.crt" +var privatekey = "certs/tls-server.com.key" +var port = ":4433" +var alpn = []string{"http/1.1"} + +func main() { + log.SetFlags(log.LstdFlags) + + println("Using GO:" + runtime.Version()) + + // Get commandline arguments + flag.StringVar(&servername, "s", "tls-server.com", "servername for SNI") + flag.StringVar(&alpn[0], "a", "http/1.1", "ALPN") + flag.StringVar(&certificate, "c", "/etc/ssl/cert-data/tls-server.com-chain.crt", "certifcate") + flag.StringVar(&privatekey, "k", "/etc/ssl/cert-data/tls-server.com.key", "private key") + flag.Parse() + println("Parameters servername=" + servername + " alpn=" + alpn[0] + " cert=" + certificate + " key=" + privatekey) + + // Load certificate and private key + cer, err := tls.LoadX509KeyPair(certificate, privatekey) + if err != nil { + log.Println(err) + return + } + + conf := &tls.Config{ + Certificates: []tls.Certificate{cer}, + MinVersion: tls.VersionTLS12, + ServerName: servername, + NextProtos: alpn, + } + + if runtime.Version() < "go1.17" { + println("Strict ALPN not implemented in go version. Overriding VerifyConnection") + // Assign a custom function for VerifyConnection + // if no ALPN is negotiated abort the handshake + // it is not possible to access the protocol sent by the client if no ALPN could be negotiated + // so it's not possible to accept the connection if no ALPN is sent + // if the wrong hostname is sent abort the connection + // if no hostname is sent continue + conf.VerifyConnection = func(cs tls.ConnectionState) error { + if cs.NegotiatedProtocol == "" { + return errors.New("INVALID ALPN") + } else if cs.ServerName != servername && len(cs.ServerName) > 0 { + return errors.New("INVALID SNI: " + cs.ServerName) + } else { + log.Println("ALPN:", cs.NegotiatedProtocol) + log.Println("SNI:", cs.ServerName) + return nil + } + } + } else { + conf.VerifyConnection = func(cs tls.ConnectionState) error { + if cs.ServerName != servername && len(cs.ServerName) > 0 { + return errors.New("INVALID SNI: " + cs.ServerName) + } else { + return nil + } + } + } + + // Listen for connections + ln, err := tls.Listen("tcp", port, conf) + if err != nil { + log.Println(err) + return + } + defer ln.Close() + + for { + conn, err := ln.Accept() + if err != nil { + log.Println(err) + continue + } + go handleConnection(conn) + } +} + +func handleConnection(conn net.Conn) { + defer conn.Close() + r := bufio.NewReader(conn) + for { + // Receive message from Client + msg, err := r.ReadString('\n') + if err != nil { + log.Println(err) + return + } + print(msg) + + // Send message to Client + n, err := conn.Write([]byte("Hello from Server!\n")) + if err != nil { + log.Println(n, err) + return + } + } +} diff --git a/evaluation-libraries/java/Dockerfile b/evaluation-libraries/java/Dockerfile new file mode 100644 index 0000000..3343077 --- /dev/null +++ b/evaluation-libraries/java/Dockerfile @@ -0,0 +1,15 @@ +# syntax=docker/dockerfile:1 +FROM tls-baseimage as tls-java +ARG VERSION=4.8.1-stable +RUN apk add openjdk11-jdk +ADD client /client +ADD server /server +WORKDIR /client/bin +RUN javac -cp ../lib/java-getopt-1.0.14.jar -d . ../src/Client.java +RUN jar cmf ../manifest Client.jar Client.class +WORKDIR /server/bin +RUN javac -cp ../lib/java-getopt-1.0.14.jar -d . ../src/Server.java +RUN jar cmf ../manifest Server.jar Server.class +WORKDIR / +COPY --from=tls-openssl /openssl-client /openssl-client +CMD ["java", "-jar", "/server/bin/Server.jar"] diff --git a/evaluation-libraries/java/LICENSE-java-getopt b/evaluation-libraries/java/LICENSE-java-getopt new file mode 100644 index 0000000..23f0d47 --- /dev/null +++ b/evaluation-libraries/java/LICENSE-java-getopt @@ -0,0 +1,481 @@ +GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/evaluation-libraries/java/README.md b/evaluation-libraries/java/README.md new file mode 100644 index 0000000..0b924ad --- /dev/null +++ b/evaluation-libraries/java/README.md @@ -0,0 +1,9 @@ +# Java tls example with strict sni and strict alpn + +Tested with openjdk11 and openjdk17 + +uses java-getopt for command-line interface https://directory.fsf.org/wiki/Java-getopt + +```bash +./run.sh +``` \ No newline at end of file diff --git a/evaluation-libraries/java/build.sh b/evaluation-libraries/java/build.sh new file mode 100755 index 0000000..53d183e --- /dev/null +++ b/evaluation-libraries/java/build.sh @@ -0,0 +1 @@ +docker build . -t tls-java -f Dockerfile \ No newline at end of file diff --git a/evaluation-libraries/java/client/README.md b/evaluation-libraries/java/client/README.md new file mode 100644 index 0000000..3edfddc --- /dev/null +++ b/evaluation-libraries/java/client/README.md @@ -0,0 +1 @@ +https://docs.oracle.com/en/java/javase/16/security/java-secure-socket-extension-jsse-reference-guide.html#GUID-59618539-24AD-431E-84E3-585C4C4BF4E5 \ No newline at end of file diff --git a/evaluation-libraries/java/client/lib/java-getopt-1.0.14.jar b/evaluation-libraries/java/client/lib/java-getopt-1.0.14.jar new file mode 100644 index 0000000..20c18c9 Binary files /dev/null and b/evaluation-libraries/java/client/lib/java-getopt-1.0.14.jar differ diff --git a/evaluation-libraries/java/client/manifest b/evaluation-libraries/java/client/manifest new file mode 100644 index 0000000..1091cec --- /dev/null +++ b/evaluation-libraries/java/client/manifest @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Client +Class-path: ../lib/java-getopt-1.0.14.jar diff --git a/evaluation-libraries/java/client/src/Client.java b/evaluation-libraries/java/client/src/Client.java new file mode 100644 index 0000000..cbfc20a --- /dev/null +++ b/evaluation-libraries/java/client/src/Client.java @@ -0,0 +1,148 @@ +import java.io.*; +import java.security.KeyStore; +import java.security.cert.*; +import java.util.*; +import javax.net.ssl.*; + +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; +import javax.naming.InvalidNameException; + +import gnu.getopt.Getopt; + +public class Client { + public static String cert = "/etc/ssl/certs/ca.crt"; + public static String[] tls_versions = new String[] { "TLSv1.2", "TLSv1.3" }; + public static String[] alpn = { "http/1.1" }; + public static String servername = "tls-server.com"; + public static String host = "127.0.0.1"; + public static int port = 4433; + + public static void main(String[] argv) throws Exception { + + // Get commandline arguments with GetOpt + Getopt g = new Getopt("Client", argv, "a:s:c:h:p:"); + int opt; + while ((opt = g.getopt()) != -1) { + switch (opt) { + case 'a': + alpn[0] = g.getOptarg(); + break; + case 's': + servername = g.getOptarg(); + break; + case 'h': + host = g.getOptarg(); + break; + case 'c': + cert = g.getOptarg(); + break; + case 'p': + port = Integer.parseInt(g.getOptarg()); + break; + default: + System.out.print("Usage: %s [-a alpn] [-s servername] [-t target] [-c certfile] [-p port]"); + } + } + System.out.println("Parameters servername=" + servername + " alpn=" + alpn[0] + " cert=" + cert + " host=" + + host + " port=" + port); + + // Create custom Keystore + File crtFile = new File(cert); + Certificate certificate = CertificateFactory.getInstance("X.509") + .generateCertificate(new FileInputStream(crtFile)); + + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, null); + keyStore.setCertificateEntry("asd", certificate); + + TrustManagerFactory trustManagerFactory = TrustManagerFactory + .getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(keyStore); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustManagerFactory.getTrustManagers(), null); + + // Use Custom Keystore + SSLSocketFactory sslsf = (SSLSocketFactory) sslContext.getSocketFactory(); + // SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault(); + SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(host, port); + SSLParameters sslp = sslSocket.getSSLParameters(); + + // set ALPN + sslp.setApplicationProtocols(alpn); + + // set SNI + SNIHostName serverName = new SNIHostName(servername); + List serverNames = new ArrayList<>(1); + serverNames.add(serverName); + sslp.setServerNames(serverNames); + + sslSocket.setSSLParameters(sslp); + sslSocket.setEnabledProtocols(tls_versions); + + // do Handshake + try { + sslSocket.startHandshake(); + } catch (javax.net.ssl.SSLHandshakeException e) { + System.out.println(e); + sslSocket.close(); + System.exit(1); + } catch (javax.net.ssl.SSLProtocolException e) { + System.out.println(e); + sslSocket.close(); + if (e.getMessage().startsWith("Invalid application_layer_protocol_negotiation")) { + System.exit(120); + } + System.exit(1); + } + + // Hostname verification + String peerCNname = getCommonName((X509Certificate) sslSocket.getSession().getPeerCertificates()[0]); + if (!peerCNname.equals(servername)) { + System.out.println("Hostname Verification failed: " + peerCNname); + System.exit(42); + } + System.out.println("Hostname Verification succeded: " + peerCNname); + + // ALPN verification + String ap = sslSocket.getApplicationProtocol(); + if (!ap.equals(alpn[0])) { + System.out.println("INVALID ALPN: \"" + ap + "\""); + System.exit(120); + } + System.out.println("ALPN: \"" + ap + "\""); + + // Send message to server + PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(sslSocket.getOutputStream()))); + out.println("Hello from Client!"); + out.flush(); + + // Receive message from server + BufferedReader in = new BufferedReader(new InputStreamReader(sslSocket.getInputStream())); + String inputLine; + inputLine = in.readLine(); + // while ((inputLine = in.readLine()) != null) + System.out.println(inputLine); + + sslSocket.close(); + } + + public static String getCommonName(X509Certificate cert) { + try { + LdapName ldapName = new LdapName(cert.getSubjectX500Principal().getName()); + /* + * Looking for the "most specific CN" (i.e. the last). + */ + String cn = null; + for (Rdn rdn : ldapName.getRdns()) { + if ("CN".equalsIgnoreCase(rdn.getType())) { + cn = rdn.getValue().toString(); + } + } + return cn; + } catch (InvalidNameException e) { + return null; + } + } +} \ No newline at end of file diff --git a/evaluation-libraries/java/docker-compose.yml b/evaluation-libraries/java/docker-compose.yml new file mode 100644 index 0000000..336a365 --- /dev/null +++ b/evaluation-libraries/java/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + java-server: + image: tls-java + openssl-server-wrong-cn: + image: tls-openssl + command: [ "/openssl-server", "-k", "/etc/ssl/cert-data/wrong-cn.com.key", "-c" , "/etc/ssl/cert-data/wrong-cn.com-chain.crt"] + openssl-malicious-alpn: + image: tls-openssl + command: [ "/openssl-server", "-m"] + java-client: + image: tls-java + command: [ "/client.sh", "java -Djavax.net.ssl.trustStore=certs/ca.crt -jar /client/bin/Client.jar", "java-server", "openssl-server-wrong-cn", "openssl-malicious-alpn" ,"1"] + depends_on: + - java-server + - openssl-server-wrong-cn + - openssl-malicious-alpn diff --git a/evaluation-libraries/java/run.sh b/evaluation-libraries/java/run.sh new file mode 100755 index 0000000..bbd5448 --- /dev/null +++ b/evaluation-libraries/java/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --abort-on-container-exit --exit-code-from java-client --remove-orphans diff --git a/evaluation-libraries/java/server/README.md b/evaluation-libraries/java/server/README.md new file mode 100644 index 0000000..3edfddc --- /dev/null +++ b/evaluation-libraries/java/server/README.md @@ -0,0 +1 @@ +https://docs.oracle.com/en/java/javase/16/security/java-secure-socket-extension-jsse-reference-guide.html#GUID-59618539-24AD-431E-84E3-585C4C4BF4E5 \ No newline at end of file diff --git a/evaluation-libraries/java/server/lib/java-getopt-1.0.14.jar b/evaluation-libraries/java/server/lib/java-getopt-1.0.14.jar new file mode 100644 index 0000000..20c18c9 Binary files /dev/null and b/evaluation-libraries/java/server/lib/java-getopt-1.0.14.jar differ diff --git a/evaluation-libraries/java/server/manifest b/evaluation-libraries/java/server/manifest new file mode 100644 index 0000000..b95175b --- /dev/null +++ b/evaluation-libraries/java/server/manifest @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Server +Class-path: ../lib/java-getopt-1.0.14.jar diff --git a/evaluation-libraries/java/server/src/Server.java b/evaluation-libraries/java/server/src/Server.java new file mode 100644 index 0000000..a47cd19 --- /dev/null +++ b/evaluation-libraries/java/server/src/Server.java @@ -0,0 +1,115 @@ +import java.io.*; +import java.security.KeyStore; +import java.util.*; +import javax.net.ssl.*; + +import gnu.getopt.Getopt; + +public class Server { + public static String keyFile = "/etc/ssl/cert-data/tls-server.com.p12"; + public static String keyPassword = "123456"; + public static String[] protocols = new String[] { "TLSv1.2", "TLSv1.3" }; + public static String[] alpn = { "http/1.1" }; + public static String servername = "tls-server.com"; + public static int port = 4433; + + public static void main(String[] argv) throws Exception { + + // Get commandline arguments with GetOpt + Getopt g = new Getopt("Server", argv, "a:s:k:p:"); + int opt; + while ((opt = g.getopt()) != -1) { + switch (opt) { + case 'a': + alpn[0] = g.getOptarg(); + break; + case 's': + servername = g.getOptarg(); + break; + case 'k': + keyFile = g.getOptarg(); + break; + case 'p': + port = Integer.parseInt(g.getOptarg()); + break; + default: + System.out.print("Usage: %s [-a alpn] [-s servername] [-t target] [-c certfile] [-p port]"); + } + } + System.out.println( + "Parameters servername=" + servername + " alpn=" + alpn[0] + " key=" + keyFile + " port=" + port); + + SSLContext ctx = SSLContext.getInstance("TLS"); + + // Create Keystore + KeyStore keyKS = KeyStore.getInstance("PKCS12"); + keyKS.load(new FileInputStream(keyFile), keyPassword.toCharArray()); + + // Generate KeyManager + KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); + kmf.init(keyKS, keyPassword.toCharArray()); + KeyManager[] kms = kmf.getKeyManagers(); + + // Initialize SSLContext using the new KeyManager + ctx.init(kms, null, null); + + // Instead of using SSLServerSocketFactory.getDefault(), + // get a SSLServerSocketFactory based on the SSLContext + SSLServerSocketFactory sslssf = ctx.getServerSocketFactory(); + SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(port); + + while (true) { + // Listen for connectionss + SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); + SSLParameters sslp = sslSocket.getSSLParameters(); + + // Set SNI hostname, the matcher aborts the connection if the servername is not + // found + SNIMatcher matcher = SNIHostName.createSNIMatcher(servername); + Collection matchers = new ArrayList<>(1); + matchers.add(matcher); + sslp.setSNIMatchers(matchers); + + // Add ALPN to the SSL parameters + // Java will abort the connection if there is a mismatch in the Protocols + sslp.setApplicationProtocols(alpn); + + sslSocket.setSSLParameters(sslp); + sslSocket.setEnabledProtocols(protocols); + + // Do the handshake + try { + sslSocket.startHandshake(); + + String ap = sslSocket.getApplicationProtocol(); + System.out.println("ALPN: \"" + ap + "\""); + + // Send message to client + PrintWriter out = new PrintWriter( + new BufferedWriter(new OutputStreamWriter(sslSocket.getOutputStream()))); + out.println("Hello from Server!"); + out.flush(); + + // Get message from client + BufferedReader in = new BufferedReader(new InputStreamReader(sslSocket.getInputStream())); + String inputLine; + while ((inputLine = in.readLine()) != null) + System.out.println(inputLine); + } catch (javax.net.ssl.SSLHandshakeException e) { + System.out.println(e); + sslSocket.close(); + continue; + } catch (java.net.SocketException e) { + System.out.println(e); + sslSocket.close(); + continue; + } catch (javax.net.ssl.SSLException e) { + System.out.println(e); + sslSocket.close(); + continue; + } + + sslSocket.close(); + } + } +} \ No newline at end of file diff --git a/evaluation-libraries/mbedtls/CMakeLists.txt b/evaluation-libraries/mbedtls/CMakeLists.txt new file mode 100644 index 0000000..52e1aea --- /dev/null +++ b/evaluation-libraries/mbedtls/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-mbedtls VERSION 0.1.0) + +add_subdirectory(client) +add_subdirectory(server) diff --git a/evaluation-libraries/mbedtls/Dockerfile b/evaluation-libraries/mbedtls/Dockerfile new file mode 100644 index 0000000..9cccb28 --- /dev/null +++ b/evaluation-libraries/mbedtls/Dockerfile @@ -0,0 +1,18 @@ +# syntax=docker/dockerfile:1 +FROM tls-baseimage as tls-mbedtls +ARG VERSION=2.18 +WORKDIR /build +RUN git clone --depth=1 --branch=archive/mbedtls-${VERSION} https://github.com/ARMmbed/mbedtls +WORKDIR /build/mbedtls +RUN git submodule update --init --recursive +RUN cmake -DCMAKE_BUILD_TYPE=Debug . && make install +WORKDIR /build +ADD server /build/server +ADD client /build/client +ADD CMakeLists.txt /build/CMakeLists.txt +RUN cmake . .. && make +RUN mv /build/server/server / +RUN mv /build/client/client / +COPY --from=tls-openssl /openssl-client /openssl-client +WORKDIR / +CMD ["/server"] \ No newline at end of file diff --git a/evaluation-libraries/mbedtls/LICENSE b/evaluation-libraries/mbedtls/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/evaluation-libraries/mbedtls/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/evaluation-libraries/mbedtls/README.md b/evaluation-libraries/mbedtls/README.md new file mode 100644 index 0000000..e2d301c --- /dev/null +++ b/evaluation-libraries/mbedtls/README.md @@ -0,0 +1,10 @@ +# mbed tls example with strict sni and strict alpn + +Tested with mbedtls 2.18 + +based on ssl_client1.c and ssl_server.c from mbedtls +https://github.com/ARMmbed/mbedtls/tree/development/programs/ssl + +```bash +./run.sh +``` \ No newline at end of file diff --git a/evaluation-libraries/mbedtls/build.sh b/evaluation-libraries/mbedtls/build.sh new file mode 100755 index 0000000..6f9e554 --- /dev/null +++ b/evaluation-libraries/mbedtls/build.sh @@ -0,0 +1 @@ +docker build --build-arg VERSION=2.18 . -t tls-mbedtls -f Dockerfile diff --git a/evaluation-libraries/mbedtls/client/CMakeLists.txt b/evaluation-libraries/mbedtls/client/CMakeLists.txt new file mode 100644 index 0000000..eb035dd --- /dev/null +++ b/evaluation-libraries/mbedtls/client/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.0.0) +project(client VERSION 0.1.0) + + +add_library(mbedtls STATIC IMPORTED) +set_target_properties(mbedtls PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/mbedtls/library/libmbedtls.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mbedtls/include" + ) +add_library(mbedx509 STATIC IMPORTED) +set_target_properties(mbedx509 PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/mbedtls/library/libmbedx509.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mbedtls/include" + ) +add_library(mbedcrypto STATIC IMPORTED) +set_target_properties(mbedcrypto PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/mbedtls/crypto/library/libmbedcrypto.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mbedtls/crypto/include" + ) + +add_executable(client client.c) +target_link_libraries(client mbedx509 mbedtls mbedcrypto) +target_compile_options(client PRIVATE -Wall -Wextra) diff --git a/evaluation-libraries/mbedtls/client/client.c b/evaluation-libraries/mbedtls/client/client.c new file mode 100644 index 0000000..f87631d --- /dev/null +++ b/evaluation-libraries/mbedtls/client/client.c @@ -0,0 +1,208 @@ +/* + * SSL client demonstration program + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#include +#include +#include +#include + +#include "mbedtls/certs.h" +#include "mbedtls/config.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/error.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/platform.h" +#include "mbedtls/x509_crt.h" + +int main(int argc, char** argv) { + // Disable buffering on stdout so docker output is shown + setbuf(stdout, NULL); + + char* host = "localhost"; + char* servername = "tls-server.com"; + char* port = "4433"; + const char* alpn[] = {"http/1.1", NULL}; + char* cert = "/etc/ssl/certs/ca.crt"; + + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:c:h:p")) != -1) { + switch (opt) { + case 'a': + alpn[0] = optarg; + break; + case 's': + servername = optarg; + break; + case 'h': + host = optarg; + break; + case 'p': + port = optarg; + break; + case 'c': + cert = optarg; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-h ip] [-p port] [-c certificate] \n", argv[0]); + exit(EXIT_FAILURE); + } + } + printf("Parameters alpn=%s servername=%s cert=%s host=%s port=%s \n", alpn[0], servername, cert, host, port); + + unsigned char buf[1024]; + int len; + int ret; + uint32_t flags; + + mbedtls_net_context server_fd; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt cacert; + + mbedtls_debug_set_threshold(1); + + const char* pers = "ssl_client1"; + + // 1. Initialize the RNG and the session data + mbedtls_net_init(&server_fd); + mbedtls_ssl_init(&ssl); + // Minimum TLS 1.2 + mbedtls_ssl_conf_min_version(&conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); + mbedtls_ssl_config_init(&conf); + mbedtls_x509_crt_init(&cacert); + + mbedtls_entropy_init(&entropy); + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers))) != 0) { + mbedtls_printf("mbedtls_ctr_drbg_seed returned %d \n", ret); + goto cleanup; + } + + // 2. Initialize ca certificate + ret = mbedtls_x509_crt_parse_file(&cacert, cert); + if (ret < 0) { + mbedtls_printf("mbedtls_x509_crt_parse returned -0x%x\n", -ret); + goto cleanup; + } + + // 3. Start the connection + if ((ret = mbedtls_net_connect(&server_fd, host, port, MBEDTLS_NET_PROTO_TCP)) != 0) { + mbedtls_printf("mbedtls_net_connect returned %d\n", ret); + goto cleanup; + } + + // 4. Setup ssl config and add ca certificate + if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + mbedtls_printf("mbedtls_ssl_config_defaults returned %d\n", ret); + goto cleanup; + } + mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + + // set ALPN + if ((ret = mbedtls_ssl_conf_alpn_protocols(&conf, alpn)) != 0) { + mbedtls_printf("mbedtls_ssl_conf_alpn_protocols returned %d\n", ret); + goto cleanup; + } + + // set SNI + if ((ret = mbedtls_ssl_set_hostname(&ssl, servername)) != 0) { + mbedtls_printf("mbedtls_ssl_set_hostname returned %d\n", ret); + goto cleanup; + } + + // enable Hostname verification + mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED); + + if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) { + mbedtls_printf("mbedtls_ssl_setup returned %d\n", ret); + goto cleanup; + } + + // 5. Handshake + // TLS 1.2 minimum + mbedtls_ssl_conf_min_version(&conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); + mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf("mbedtls_ssl_handshake returned -0x%x\n", ret); + goto cleanup; + } + } + + // get ALPN: + const char* alpn_selected = mbedtls_ssl_get_alpn_protocol(&ssl); + printf("ALPN: %s \n", alpn_selected); + + //6. Verify the server certificate + if ((flags = mbedtls_ssl_get_verify_result(&ssl)) != 0) { + char vrfy_buf[512]; + mbedtls_printf("mbedtls_ssl_get_verify_result failed\n"); + mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags); + mbedtls_printf("%s\n", vrfy_buf); + goto cleanup; + } + + // 7. sent message to server + len = sprintf((char*)buf, "Hello from Client!\n"); + while ((ret = mbedtls_ssl_write(&ssl, buf, len)) <= 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf("mbedtls_ssl_write returned %d\n", ret); + goto cleanup; + } + ret = 0; + } + // 8. get message from server + len = sizeof(buf) - 1; + memset(buf, 0, sizeof(buf)); + ret = mbedtls_ssl_read(&ssl, buf, len); + if (ret < 0) { + mbedtls_printf("mbedtls_ssl_read returned %d\n", ret); + } else { + mbedtls_printf((char*)buf); + ret = 0; + } + + mbedtls_ssl_close_notify(&ssl); +cleanup: + mbedtls_net_free(&server_fd); + mbedtls_x509_crt_free(&cacert); + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + if (ret != 0) { + char error_buf[100]; + mbedtls_strerror(ret, error_buf, 100); + if (strstr(error_buf, "X509 - Certificate verification failed") != NULL) { + ret = 42; + } else if (strstr(error_buf, "Processing of the ServerHello handshake message failed") != NULL) { + ret = 120; + } + mbedtls_printf("Last error was: %d - %s\n", ret, error_buf); + return ret; + } + return 0; +} \ No newline at end of file diff --git a/evaluation-libraries/mbedtls/docker-compose.yml b/evaluation-libraries/mbedtls/docker-compose.yml new file mode 100644 index 0000000..4c0fc46 --- /dev/null +++ b/evaluation-libraries/mbedtls/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + mbedtls-server: + image: tls-mbedtls + openssl-server-wrong-cn: + image: tls-openssl + command: [ "/openssl-server", "-k", "/etc/ssl/cert-data/wrong-cn.com.key", "-c" , "/etc/ssl/cert-data/wrong-cn.com-chain.crt"] + openssl-malicious-alpn: + image: tls-openssl + command: [ "/openssl-server", "-m"] + mbedtls-client: + image: tls-mbedtls + command: [ "./client.sh", "/client", "mbedtls-server", "openssl-server-wrong-cn", "openssl-malicious-alpn" ,"1"] + depends_on: + - mbedtls-server + - openssl-server-wrong-cn + - openssl-malicious-alpn \ No newline at end of file diff --git a/evaluation-libraries/mbedtls/run.sh b/evaluation-libraries/mbedtls/run.sh new file mode 100755 index 0000000..b432af3 --- /dev/null +++ b/evaluation-libraries/mbedtls/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --abort-on-container-exit --exit-code-from mbedtls-client --remove-orphans \ No newline at end of file diff --git a/evaluation-libraries/mbedtls/server/CMakeLists.txt b/evaluation-libraries/mbedtls/server/CMakeLists.txt new file mode 100644 index 0000000..9826f6f --- /dev/null +++ b/evaluation-libraries/mbedtls/server/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.0.0) +project(server VERSION 0.1.0) + + +add_library(mbedtls STATIC IMPORTED) +set_target_properties(mbedtls PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/mbedtls/library/libmbedtls.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mbedtls/include" + ) +add_library(mbedx509 STATIC IMPORTED) +set_target_properties(mbedx509 PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/mbedtls/library/libmbedx509.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mbedtls/include" + ) +add_library(mbedcrypto STATIC IMPORTED) +set_target_properties(mbedcrypto PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/mbedtls/crypto/library/libmbedcrypto.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mbedtls/crypto/include" + ) + +add_executable(server server.c) +target_link_libraries(server mbedx509 mbedtls mbedcrypto) +target_compile_options(server PRIVATE -Wall -Wextra) diff --git a/evaluation-libraries/mbedtls/server/server.c b/evaluation-libraries/mbedtls/server/server.c new file mode 100644 index 0000000..79d3404 --- /dev/null +++ b/evaluation-libraries/mbedtls/server/server.c @@ -0,0 +1,235 @@ +/* + * SSL server demonstration program + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#include +#include +#include + +#include "mbedtls/certs.h" +#include "mbedtls/config.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/error.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/platform.h" +#include "mbedtls/ssl.h" +#include "mbedtls/ssl_cache.h" +#include "mbedtls/x509.h" + +mbedtls_entropy_context entropy; +mbedtls_ctr_drbg_context ctr_drbg; +mbedtls_ssl_context ssl; +mbedtls_ssl_config conf; +mbedtls_x509_crt srvcert; +mbedtls_pk_context pkey; + +// needs to return 0 if a valid SNI was found and set the correct certificate +int sni_callback(void *p_info, mbedtls_ssl_context *ssl, const unsigned char *name, size_t name_len) { + if (strcmp((const char *)name, (const char *)p_info) == 0) { + mbedtls_printf("VALID SNI: %s \n", name); + mbedtls_ssl_set_hs_own_cert(ssl, &srvcert, &pkey); + return 0; + } else { + mbedtls_printf("INVALID SNI: %s \n", name); + return -1; + } +} + +int main(int argc, char **argv) { + // Disable buffering on stdout so docker output is shown + setbuf(stdout, NULL); + + char *servername = "tls-server.com"; + char *port = "4433"; + const char *alpn[] = {"http/1.1", NULL}; + char *cert = "/etc/ssl/cert-data/tls-server.com-chain.crt"; + char *key = "/etc/ssl/cert-data/tls-server.com.key"; + + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:c:k:p")) != -1) { + switch (opt) { + case 'a': + alpn[0] = optarg; + break; + case 's': + servername = optarg; + break; + case 'k': + key = optarg; + break; + case 'p': + port = optarg; + break; + case 'c': + cert = optarg; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-h ip] [-p port] [-c certificate] \n", argv[0]); + exit(EXIT_FAILURE); + } + } + printf("Parameters alpn=%s servername=%s cert=%s key=%s port=%s \n", alpn[0], servername, cert, key, port); + + int ret, len; + mbedtls_net_context listen_fd, client_fd; + unsigned char buf[1024]; + const char *pers = "ssl_server"; + + mbedtls_net_init(&listen_fd); + mbedtls_net_init(&client_fd); + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); + mbedtls_x509_crt_init(&srvcert); + mbedtls_pk_init(&pkey); + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + + // 1. Load the certificates and private RSA key + ret = mbedtls_x509_crt_parse_file(&srvcert, cert); + if (ret != 0) { + mbedtls_printf("mbedtls_x509_crt_parse_file returned %d\n", ret); + goto exit; + } + ret = mbedtls_pk_parse_keyfile(&pkey, key, NULL); + if (ret != 0) { + mbedtls_printf("mbedtls_pk_parse_keyfile returned %d\n", ret); + goto exit; + } + + // 2. Setup the listening TCP socket + if ((ret = mbedtls_net_bind(&listen_fd, NULL, port, MBEDTLS_NET_PROTO_TCP)) != 0) { + mbedtls_printf("mbedtls_net_bind returned %d\n", ret); + goto exit; + } + + // 3. Seed the RNG + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)pers, strlen(pers))) != 0) { + mbedtls_printf("mbedtls_ctr_drbg_seed returned %d\n", ret); + goto exit; + } + + // 4. Setup SSL config + if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + mbedtls_printf("mbedtls_ssl_config_defaults returned %d\n", ret); + goto exit; + } + + // Set ALPN + if ((ret = mbedtls_ssl_conf_alpn_protocols(&conf, alpn)) != 0) { + mbedtls_printf("mbedtls_ssl_conf_alpn_protocols returned %d\n", ret); + goto exit; + } + + // set SNI callback function + mbedtls_ssl_conf_sni(&conf, sni_callback, servername); + + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + + mbedtls_ssl_conf_ca_chain(&conf, srvcert.next, NULL); + if ((ret = mbedtls_ssl_conf_own_cert(&conf, &srvcert, &pkey)) != 0) { + mbedtls_printf("mbedtls_ssl_conf_own_cert returned %d\n", ret); + goto exit; + } + + if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) { + mbedtls_printf("mbedtls_ssl_setup returned %d\n", ret); + goto exit; + } + +reset: + if (ret != 0) { + char error_buf[100]; + mbedtls_strerror(ret, error_buf, 100); + mbedtls_printf("Last error was: %d - %s\n", ret, error_buf); + } + mbedtls_net_free(&client_fd); + mbedtls_ssl_session_reset(&ssl); + + // 5. Wait until a client connects + if ((ret = mbedtls_net_accept(&listen_fd, &client_fd, NULL, 0, NULL)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_net_accept returned %d\n", ret); + goto exit; + } + mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + + // TLS 1.2 minimum + mbedtls_ssl_conf_min_version(&conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); + // 6. Handshake + while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf("mbedtls_ssl_handshake returned %d\n", ret); + goto reset; + } + } + + // get ALPN: + const char *alpn_selected = mbedtls_ssl_get_alpn_protocol(&ssl); + printf("ALPN: %s \n", alpn_selected); + + // 6. Get message from client + len = sizeof(buf) - 1; + memset(buf, 0, sizeof(buf)); + ret = mbedtls_ssl_read(&ssl, buf, len); + if (ret < 0) { + mbedtls_printf("failed\n ! mbedtls_ssl_read returned %d\n\n", ret); + } else { + mbedtls_printf((char *)buf); + } + + // 7. Send response to client + len = sprintf((char *)buf, "Hello from Server!\n"); + + while ((ret = mbedtls_ssl_write(&ssl, buf, len)) <= 0) { + if (ret == MBEDTLS_ERR_NET_CONN_RESET) { + mbedtls_printf("peer closed the connection\n"); + goto reset; + } + + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf("mbedtls_ssl_write returned %d\n", ret); + goto exit; + } + } + while ((ret = mbedtls_ssl_close_notify(&ssl)) < 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf("mbedtls_ssl_close_notify returned %d\n", ret); + goto reset; + } + } + + ret = 0; + goto reset; + +exit: + mbedtls_net_free(&client_fd); + mbedtls_net_free(&listen_fd); + + mbedtls_x509_crt_free(&srvcert); + mbedtls_pk_free(&pkey); + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + + return (ret); +} \ No newline at end of file diff --git a/evaluation-libraries/openssl/.gitignore b/evaluation-libraries/openssl/.gitignore new file mode 100644 index 0000000..a17f9ee --- /dev/null +++ b/evaluation-libraries/openssl/.gitignore @@ -0,0 +1,19 @@ +build +.vscode +CPackConfig.cmake +CPackSourceConfig.cmake +DartConfiguration.tcl +boringssl2 +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +client/178.crt diff --git a/evaluation-libraries/openssl/CMakeLists.txt b/evaluation-libraries/openssl/CMakeLists.txt new file mode 100644 index 0000000..ba6227c --- /dev/null +++ b/evaluation-libraries/openssl/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-boringssl VERSION 0.1.0) + +add_subdirectory(client) +add_subdirectory(server) diff --git a/evaluation-libraries/openssl/Dockerfile-boringssl b/evaluation-libraries/openssl/Dockerfile-boringssl new file mode 100644 index 0000000..5d442ec --- /dev/null +++ b/evaluation-libraries/openssl/Dockerfile-boringssl @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:1 +FROM tls-baseimage as tls-boringssl +ARG VERSION=3945 +RUN git clone --depth=1 https://boringssl.googlesource.com/boringssl +#RUN git clone --depth=1 -b ${VERSION} https://boringssl.googlesource.com/boringssl +WORKDIR /src/boringssl +RUN cmake . && make +RUN mv crypto/libcrypto.a /lib/libcrypto.a +RUN mv ssl/libssl.a /lib/libssl.a +RUN mv include/* /usr/include/ + +WORKDIR /build +ADD server /build/server +ADD client /build/client +ADD CMakeLists.txt /build/CMakeLists.txt +RUN cmake . .. && make +RUN mv /build/server/server / +RUN mv /build/client/client / +COPY --from=tls-openssl /openssl-client /openssl-client +WORKDIR / +CMD ["/server"] \ No newline at end of file diff --git a/evaluation-libraries/openssl/Dockerfile-openssl b/evaluation-libraries/openssl/Dockerfile-openssl new file mode 100644 index 0000000..c87566c --- /dev/null +++ b/evaluation-libraries/openssl/Dockerfile-openssl @@ -0,0 +1,26 @@ +# syntax=docker/dockerfile:1 +FROM tls-baseimage as tls-openssl +#ARG VERSION=1_1_1 +#RUN git clone --depth=1 -b OpenSSL_${VERSION}-stable https://github.com/openssl/openssl +RUN git clone --depth=1 -b openssl-3.0 https://github.com/openssl/openssl +WORKDIR /src/openssl +RUN ./config no-async +RUN make +RUN make install +RUN cp libcrypto.a /lib/libcrypto.a +RUN cp libssl.a /lib/libssl.a +RUN cp -r -L include/* /usr/include/ + +WORKDIR /build +ADD server /build/server +ADD client /build/client +ADD CMakeLists.txt /build/CMakeLists.txt +RUN cmake . .. && make +RUN mv /build/server/server /openssl-server +RUN mv /build/client/client /openssl-client +RUN rm -r /build/* +RUN rm -r /usr/include/openssl +RUN rm /lib/libcrypto.a +RUN rm /lib/libssl.a +WORKDIR / +CMD ["/openssl-server"] \ No newline at end of file diff --git a/evaluation-libraries/openssl/LICENSE b/evaluation-libraries/openssl/LICENSE new file mode 100644 index 0000000..5b5ccdc --- /dev/null +++ b/evaluation-libraries/openssl/LICENSE @@ -0,0 +1,124 @@ + + LICENSE ISSUES + ============== + + The OpenSSL toolkit stays under a double license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ diff --git a/evaluation-libraries/openssl/README.md b/evaluation-libraries/openssl/README.md new file mode 100644 index 0000000..04c02cf --- /dev/null +++ b/evaluation-libraries/openssl/README.md @@ -0,0 +1,13 @@ +# openssl and boringssl example with strict sni and strict alpn + +This library creates containers for openssl and boringssl since they are almost code-compatible. + +needs tls-baseimage already in docker + +Tested openSSL 1.1.0, 1.1.1, 3.0 and BoringSSL/master from November 2021 + +roughly based on https://wiki.openssl.org/index.php/SSL/TLS_Client and https://wiki.openssl.org/index.php/Simple_TLS_Server + +```bash +./run.sh +``` \ No newline at end of file diff --git a/evaluation-libraries/openssl/build.sh b/evaluation-libraries/openssl/build.sh new file mode 100755 index 0000000..4138244 --- /dev/null +++ b/evaluation-libraries/openssl/build.sh @@ -0,0 +1,2 @@ +docker build --build-arg VERSION=1_1_1 . -t tls-openssl -f Dockerfile-openssl +docker build --build-arg VERSION=3945 . -t tls-boringssl -f Dockerfile-boringssl diff --git a/evaluation-libraries/openssl/client/CMakeLists.txt b/evaluation-libraries/openssl/client/CMakeLists.txt new file mode 100644 index 0000000..c5f19c5 --- /dev/null +++ b/evaluation-libraries/openssl/client/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.0.0) +project(client VERSION 0.1.0) + +find_package(OpenSSL REQUIRED) + +add_executable(client client.c) +target_link_libraries(client ${CMAKE_DL_LIBS} ssl crypto pthread dl) +target_compile_options(client PRIVATE -Wall -Wextra) \ No newline at end of file diff --git a/evaluation-libraries/openssl/client/client.c b/evaluation-libraries/openssl/client/client.c new file mode 100644 index 0000000..6f451b1 --- /dev/null +++ b/evaluation-libraries/openssl/client/client.c @@ -0,0 +1,241 @@ +#include "client.h" + +char *alpn = "http/1.1"; +char *host = "127.0.0.1"; +char *port = "4433"; +char *servername = "tls-server.com"; +char *cert = "/etc/ssl/certs/ca.crt"; + +int debug = 0; + +int main(int argc, char *argv[]) { + // Disable buffering on stdout so docker output is shown + setbuf(stdout, NULL); + + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:c:h:p:d")) != -1) { + switch (opt) { + case 'a': + alpn = optarg; + break; + case 's': + servername = optarg; + break; + case 'h': + host = optarg; + break; + case 'p': + port = optarg; + break; + case 'c': + cert = optarg; + break; + case 'd': + debug = 1; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-h ip] [-p port] [-c certificate] \n", argv[0]); + exit(EXIT_FAILURE); + } + } + printf("Parameters alpn=%s servername=%s cert=%s host=%s port=%s \n", alpn, servername, cert, host, port); + + /* Create SSL Context */ +#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); +#else + SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_method()); +#endif + /* Set TLS Version */ +#ifdef TLS1_3_VERSION + if (!SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION)) { + exit(2); + } + if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) { + exit(2); + } +#endif + + // Format alpn to wire-format ("http/1.1" -> "8http/1.1") + unsigned char *alpn_formatted; + size_t alpn_formatted_len = strlen(alpn) + 1; + alpn_formatted = calloc(alpn_formatted_len, sizeof(unsigned char)); + alpn_formatted[0] = (unsigned char)strlen(alpn); + for (size_t i = 1; i <= strlen(alpn); i++) { + alpn_formatted[i] = alpn[i - 1]; + } + + if (SSL_CTX_set_alpn_protos(ctx, alpn_formatted, alpn_formatted_len)) { + fprintf(stderr, "Failed to set ALPN.\n"); + exit(3); + } + + /* Load certificate file */ + if (!SSL_CTX_load_verify_locations(ctx, cert, NULL)) { + fprintf(stderr, "Failed to load cert.\n"); + exit(4); + } + + /* debug handshake */ + if (debug) { + SSL_CTX_set_info_callback(ctx, InfoCallback); + } + + /* Connect to server */ + char *host_with_port = calloc(sizeof(char), strlen(host) + strlen(port) + 1); + strcat(host_with_port, host); + strcat(host_with_port, ":"); + strcat(host_with_port, port); + BIO *bio = BIO_new_connect(host_with_port); + SSL *ssl = SSL_new(ctx); + +/* 3. Hostname */ +#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x10100000L + X509_VERIFY_PARAM *param = SSL_get0_param(ssl); + X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + if (!X509_VERIFY_PARAM_set1_host(param, servername, strlen(servername))) { + fprintf(stderr, "X509_VERIFY_PARAM_set1_host\n"); + exit(5); + } +#else + SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + if (!SSL_set1_host(ssl, servername)) { + fprintf(stderr, "SSL_set1_host\n"); + exit(5); + } +#endif + SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL); + + /* Set SNI */ + SSL_set_tlsext_host_name(ssl, servername); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + SSL_set0_wbio(ssl, bio); + SSL_set0_rbio(ssl, bio); +#else + SSL_set_bio(ssl, bio, bio); +#endif + + //bio.release(); + int ret = SSL_connect(ssl); + if (ret != 1) { + //int ssl_err = SSL_get_error(ssl, ret); + //PrintSSLError(stderr, "Error while connecting", ssl_err, ret); + int return_value = SSL_ERROR_SSL; + ERR_print_errors_cb(&error_callback, &return_value); + exit(return_value); + } + printf("Connected! \n"); + + /* 1. Verify Certificate */ + X509 *ServerCert = SSL_get_peer_certificate(ssl); + if (ServerCert) { + X509_free(ServerCert); + } else { + fprintf(stderr, "SSL_get_peer_certificate.\n"); + exit(7); + } + + /* 2. Verify Certificate Chain */ + if (SSL_get_verify_result(ssl) != X509_V_OK) { + fprintf(stderr, "Error in SSL_get_verify_result.\n"); + exit(8); + } + + // strict ALPN verification + const unsigned char *alpn_received; + unsigned int alpn_received_len; + SSL_get0_alpn_selected(ssl, &alpn_received, &alpn_received_len); + + if (alpn_received_len > 0) { + if (strncmp((const char *)alpn_received, alpn, alpn_received_len) == 0) { + printf("ALPN: %.*s \n", alpn_received_len, alpn_received); + } else { + fprintf(stderr, "INVALID ALPN: %.*s \n", alpn_received_len, alpn_received); + return TLS1_AD_NO_APPLICATION_PROTOCOL; + } + } else { + printf("No ALPN negotiated ! %.*s\n", alpn_received_len, alpn_received); + //exit(10); + } + + /* send message to server */ + char *message = "Hello from Client!\n"; + SSL_write(ssl, message, strlen(message)); + + /* get message from server */ + char buff[1536] = {}; + SSL_read(ssl, buff, sizeof(buff)); + printf("%s", buff); + + /* Close connection */ + BIO_free(bio); + SSL_CTX_free(ctx); + + return 0; +} + +/* Debug info callback for handshake */ +static void InfoCallback(const SSL *ssl, int type, __attribute__((unused)) int value) { + switch (type) { + case SSL_CB_HANDSHAKE_START: + printf("Handshake started\n"); + break; + case SSL_CB_HANDSHAKE_DONE: + printf("Handshake done\n"); + break; + case SSL_CB_CONNECT_LOOP: + printf("Handshake progress: %s\n", SSL_state_string_long(ssl)); + break; + } +} + +static int error_callback(const char *str, size_t len, void *err) { + if (strstr(str, "SSL alert number 120") != NULL || strstr(str, "INVALID_ALPN_PROTOCOL") != NULL) { + printf("TLSV1_ALERT_NO_APPLICATION_PROTOCOL \n"); + //err = 1; + (*(int *)err) = TLS1_AD_NO_APPLICATION_PROTOCOL; + } else if (strstr(str, "CERTIFICATE_VERIFY_FAILED") != NULL || strstr(str, "certificate verify failed") != NULL) { + printf("CERTIFICATE_VERIFY_FAILED \n"); + //err = 1; + (*(int *)err) = SSL3_AD_BAD_CERTIFICATE; + } else if (strstr(str, "TLSV1_ALERT_UNRECOGNIZED_NAME") != NULL || strstr(str, "tlsv1 unrecognized name") != NULL) { + printf("TLSV1_ALERT_UNRECOGNIZED_NAME \n"); + //err = 1; + (*(int *)err) = TLS1_AD_UNRECOGNIZED_NAME; + } else { + printf("%s", str); + (*(int *)err) = SSL_ERROR_SSL; + } + return 0; +} + +void PrintSSLError(FILE *file, const char *msg, int ssl_err, int ret) { + switch (ssl_err) { + case SSL_ERROR_SSL: + fprintf(stderr, " %s \n", ERR_reason_error_string(ERR_peek_error())); + break; + case SSL_ERROR_SYSCALL: + if (ret == 0) { + fprintf(stderr, " peer closed connection \n"); + } else { + char *error = strerror(errno); + fprintf(stderr, " %s %s \n", msg, error); + } + break; + case SSL_ERROR_ZERO_RETURN: + fprintf(stderr, "received close_notify \n"); + break; + default: + break; +#ifdef OPENSSL_IS_BORINGSSL + fprintf(file, "%s: unexpected error: %s\n", msg, SSL_error_description(ssl_err)); +#else + fprintf(file, "%s: unexpected error: %s\n", msg, ERR_reason_error_string(ssl_err)); +#endif + } + //ERR_print_errors_fp(file); + int err = 5; + ERR_print_errors_cb(&error_callback, &err); +} \ No newline at end of file diff --git a/evaluation-libraries/openssl/client/client.h b/evaluation-libraries/openssl/client/client.h new file mode 100644 index 0000000..659b4f4 --- /dev/null +++ b/evaluation-libraries/openssl/client/client.h @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include +#include +#include + +void PrintSSLError(FILE *file, const char *msg, int ssl_err, int ret); + +static void InfoCallback(const SSL *ssl, int type, int value); + +static int error_callback(const char *str, size_t len, void *err); \ No newline at end of file diff --git a/evaluation-libraries/openssl/docker-compose-boringssl.yml b/evaluation-libraries/openssl/docker-compose-boringssl.yml new file mode 100644 index 0000000..7e116f6 --- /dev/null +++ b/evaluation-libraries/openssl/docker-compose-boringssl.yml @@ -0,0 +1,36 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + openssl-server-wrong-cn: + image: tls-openssl + command: + [ + "/openssl-server", + "-k", + "/etc/ssl/cert-data/wrong-cn.com.key", + "-c", + "/etc/ssl/cert-data/wrong-cn.com-chain.crt", + ] + openssl-malicious-alpn: + image: tls-openssl + command: ["/openssl-server", "-m"] + boringssl-server: + image: tls-boringssl + boringssl-client: + image: tls-boringssl + depends_on: + - boringssl-server + - openssl-server-wrong-cn + - openssl-malicious-alpn + command: + [ + "/client.sh", + "/client", + "boringssl-server", + "openssl-server-wrong-cn", + "openssl-malicious-alpn", + "6", + ] diff --git a/evaluation-libraries/openssl/docker-compose.yml b/evaluation-libraries/openssl/docker-compose.yml new file mode 100644 index 0000000..315cb4b --- /dev/null +++ b/evaluation-libraries/openssl/docker-compose.yml @@ -0,0 +1,36 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + openssl-server: + image: tls-openssl + openssl-server-wrong-cn: + image: tls-openssl + command: + [ + "/openssl-server", + "-k", + "/etc/ssl/cert-data/wrong-cn.com.key", + "-c", + "/etc/ssl/cert-data/wrong-cn.com-chain.crt", + ] + openssl-malicious-alpn: + image: tls-openssl + command: ["/openssl-server", "-m"] + openssl-client: + image: tls-openssl + depends_on: + - openssl-server + - openssl-server-wrong-cn + - openssl-malicious-alpn + command: + [ + "/client.sh", + "/openssl-client", + "openssl-server", + "openssl-server-wrong-cn", + "openssl-malicious-alpn", + "1", + ] diff --git a/evaluation-libraries/openssl/run.sh b/evaluation-libraries/openssl/run.sh new file mode 100755 index 0000000..a494150 --- /dev/null +++ b/evaluation-libraries/openssl/run.sh @@ -0,0 +1,3 @@ +./build.sh +docker-compose up --exit-code-from openssl-client --remove-orphans +docker-compose -f docker-compose-boringssl.yml -p "boringssl" up --exit-code-from boringssl-client --remove-orphans \ No newline at end of file diff --git a/evaluation-libraries/openssl/server/CMakeLists.txt b/evaluation-libraries/openssl/server/CMakeLists.txt new file mode 100644 index 0000000..d6e2a52 --- /dev/null +++ b/evaluation-libraries/openssl/server/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.0.0) +project(server VERSION 0.1.0) + +find_package(OpenSSL REQUIRED) +add_executable(server server.c) +target_link_libraries(server ${CMAKE_DL_LIBS} ssl crypto pthread dl) +target_compile_options(server PRIVATE -Wall -Wextra) diff --git a/evaluation-libraries/openssl/server/server.c b/evaluation-libraries/openssl/server/server.c new file mode 100644 index 0000000..f3074c6 --- /dev/null +++ b/evaluation-libraries/openssl/server/server.c @@ -0,0 +1,226 @@ +#include "server.h" + +char *alpn = "http/1.1"; +char *servername = "tls-server.com"; +char *cert = "/etc/ssl/cert-data/tls-server.com.crt"; +char *key = "/etc/ssl/cert-data/tls-server.com.key"; + +const uint16_t port = 4433; + +int malicious_alpn = 0; + +int err, ret; + +int main(int argc, char **argv) { + // Disable buffering on stdout so docker output is shown + setbuf(stdout, NULL); + + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:c:k:m")) != -1) { + switch (opt) { + case 'a': + alpn = optarg; + break; + case 's': + servername = optarg; + break; + case 'c': + cert = optarg; + break; + case 'k': + key = optarg; + break; + case 'm': + malicious_alpn = 1; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-c certfile] [-k keyfile] \n", + argv[0]); + exit(EXIT_FAILURE); + } + } + printf("Parameters alpn=%s servername=%s cert=%s key=%s port=%d \n", alpn, servername, cert, key, port); + + int sock; +#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); +#else + SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_method()); +#endif + /* Set TLS Version */ +#ifdef TLS1_3_VERSION + if (!SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION)) { + exit(2); + } + if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) { + exit(2); + } +#endif + + // Enable alpn callback function + SSL_CTX_set_alpn_select_cb(ctx, alpn_cb, NULL); + + // Enable SNI callback function + SSL_CTX_set_tlsext_servername_callback(ctx, sni_cb); + + // Load certificate chain and key from file + if (SSL_CTX_use_certificate_chain_file(ctx, cert) <= 0) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } + if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) <= 0) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } + + sock = create_socket(port); + + /* Handle connections */ + while (1) { + struct sockaddr_in addr; + unsigned int len = sizeof(addr); + SSL *ssl; + + int client = accept(sock, (struct sockaddr *)&addr, &len); + if (client < 0) { + perror("Unable to accept"); + exit(EXIT_FAILURE); + } + + ssl = SSL_new(ctx); + SSL_set_fd(ssl, client); + + SSL_accept(ssl); + // err = SSL_get_error(ssl, 0); + // printf("ERROR: %d \n", err); + // if (err == SSL_ERROR_SYSCALL || err == SSL_ERROR_SSL) { + // SSL_free(ssl); + // close(client); + // continue; + // } + + // get message from client + char buff[1536] = {}; + len = SSL_read(ssl, buff, sizeof(buff)); + if(len <= 0) { + int errorvalue = SSL_get_error(ssl, len); + if (errorvalue != SSL_ERROR_NONE) { + printf("Errorvalue: %d return of read: %d \n", errorvalue, len); + } + + SSL_free(ssl); + close(client); + + continue; + } + printf("%s \n", buff); + // err = SSL_get_error(ssl, 0); + // printf("ERROR: %d \n", err); + // if (err == SSL_ERROR_SYSCALL || err == SSL_ERROR_SSL) { + // SSL_free(ssl); + // close(client); + // continue; + // } + + // send message to client + char *message = "Hello from Server!\n"; + + len = SSL_write(ssl, message, strlen(message)); + if (len <= 0) { + SSL_free(ssl); + close(client); + continue; + } + // err = SSL_get_error(ssl, 0); + // printf("ERROR: %d \n", err); + // if (err == SSL_ERROR_SYSCALL || err == SSL_ERROR_SSL) { + // SSL_free(ssl); + // close(client); + // continue; + // } + + SSL_shutdown(ssl); + SSL_free(ssl); + close(client); + } + + close(sock); + SSL_CTX_free(ctx); + return 0; +} + +#ifdef OPENSSL_IS_BORINGSSL +static int alpn_cb(__attribute__((unused)) SSL *ssl, const uint8_t **out, uint8_t *outlen, const uint8_t *in, unsigned inlen, __attribute__((unused)) void *arg) { +#else +static int alpn_cb(__attribute__((unused)) SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, __attribute__((unused)) void *arg) { +#endif + // Return the alpn "invalid" in the ServerHello message, used for running Test5 + if (malicious_alpn == 1) { + unsigned char *invalidalpn = (unsigned char *)"invalid"; + out[0] = invalidalpn; + outlen[0] = 7; + return SSL_TLSEXT_ERR_OK; + } + + // Format alpn to wire-format ("http/1.1" -> "8http/1.1") + unsigned char *alpn_formatted; + size_t alpn_formatted_len = strlen(alpn) + 1; + alpn_formatted = calloc(alpn_formatted_len, sizeof(unsigned char)); + alpn_formatted[0] = (unsigned char)strlen(alpn); + for (size_t i = 1; i <= strlen(alpn); i++) { + alpn_formatted[i] = alpn[i - 1]; + } + + if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_formatted, alpn_formatted_len, in, inlen) == OPENSSL_NPN_NEGOTIATED) { + printf("ALPN: %s \n", *out); + return SSL_TLSEXT_ERR_OK; + } else { + printf("INVALID ALPN: %s \n", *out); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } +} + +static int sni_cb(SSL *s, __attribute__((unused)) int *al, __attribute__((unused)) void *arg) { + const char *servername_received = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + + if (servername_received != NULL) { + if (strcasecmp(servername_received, servername) == 0) { + printf("SNI: %s \n", servername_received); + return SSL_TLSEXT_ERR_OK; + } else { + printf("INVALID SNI: %s \n", servername_received); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + } else { + printf("no SNI sent by client, continuing. \n"); + return SSL_TLSEXT_ERR_OK; + } +} + +int create_socket(uint16_t p) { + int s; + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_port = htons(p); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + perror("Unable to create socket"); + exit(EXIT_FAILURE); + } + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Unable to bind"); + exit(EXIT_FAILURE); + } + + if (listen(s, 1) < 0) { + perror("Unable to listen"); + exit(EXIT_FAILURE); + } + + return s; +} diff --git a/evaluation-libraries/openssl/server/server.h b/evaluation-libraries/openssl/server/server.h new file mode 100644 index 0000000..cf623af --- /dev/null +++ b/evaluation-libraries/openssl/server/server.h @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int create_socket(uint16_t p); + +#ifdef OPENSSL_IS_BORINGSSL +static int alpn_cb(SSL *ssl, const uint8_t **out, uint8_t *out_len, const uint8_t *in, unsigned in_len, void *arg); +#else +static int alpn_cb(SSL *ssl, const unsigned char **out, unsigned char *out_len, const unsigned char *in, unsigned int in_len, void *arg); +#endif + +static int sni_cb(SSL *s, int *al, void *arg); diff --git a/evaluation-libraries/run-everything.sh b/evaluation-libraries/run-everything.sh new file mode 100755 index 0000000..f1ddfe9 --- /dev/null +++ b/evaluation-libraries/run-everything.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# validate current path +CURRENT=`pwd` +BASENAME=`basename "$CURRENT"` +if [ "$BASENAME" != "evaluation-libraries" ]; then + echo "Please start from the evaluation-libraries folder" + exit +fi + +RED='\033[0;31m ' +GREEN='\033[0;32m ' +NC='\033[0m' # No Color + + +./build-everything.sh + +# go into every library folder +# 1. run containers and tests +# 2. get results file from docker container +# 3. append them to the results file on the host +rm results +for library in bearssl botan gnutls java golang mbedtls openssl wolfssl rustls ; do + (cd "$library" + ./run.sh + containerid=$(docker-compose ps -q $library-client) + echo "Getting results file from container :$containerid" + docker cp $containerid:/results results-temp + echo -e "${NC}$library" >> ../results + cat results-temp >> ../results + rm results-temp + ); +done + +# boringssl is included in the openssl folder so we need to get the file manually +cd openssl +containerid=$(docker-compose -f docker-compose-boringssl.yml -p "boringssl" ps -q boringssl-client) +echo "Getting results file from container :$containerid" +docker cp $containerid:/results results-temp +echo -e "${NC}boringssl" >> ../results +cat results-temp >> ../results +rm results-temp +cd .. + +cat results +#remove colors from output +sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g" results > results-temp +mv results-temp results diff --git a/evaluation-libraries/rustls/Cargo.toml b/evaluation-libraries/rustls/Cargo.toml new file mode 100644 index 0000000..5457677 --- /dev/null +++ b/evaluation-libraries/rustls/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "alpaca-rustls" +version = "0.0.1" +edition = "2018" +description = "Rustls example." +publish = false + +[dependencies] +rustls = "0.20.0" +rustls-pemfile = "0.2.0" +clap = "~2.27.0" +mio = { version = "0.7", features = ["os-poll", "tcp"] } + + +[[bin]] +name = "server" +path = "server/server.rs" + +[[bin]] +name = "client" +path = "client/client.rs" \ No newline at end of file diff --git a/evaluation-libraries/rustls/Dockerfile b/evaluation-libraries/rustls/Dockerfile new file mode 100644 index 0000000..2ca96ca --- /dev/null +++ b/evaluation-libraries/rustls/Dockerfile @@ -0,0 +1,15 @@ +# syntax=docker/dockerfile:1 +FROM tls-baseimage as tls-rustls +RUN apk add cargo +WORKDIR /build +ADD server /build/server +ADD client /build/client +ADD Cargo.toml /build/Cargo.toml +RUN cargo build --release + +RUN mv /build/target/release/server /server +RUN mv /build/target/release/client /client + +COPY --from=tls-openssl /openssl-client /openssl-client +WORKDIR / +CMD ["/server"] diff --git a/evaluation-libraries/rustls/LICENSE b/evaluation-libraries/rustls/LICENSE new file mode 100644 index 0000000..f8e5e5e --- /dev/null +++ b/evaluation-libraries/rustls/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. \ No newline at end of file diff --git a/evaluation-libraries/rustls/README.md b/evaluation-libraries/rustls/README.md new file mode 100644 index 0000000..4713b1f --- /dev/null +++ b/evaluation-libraries/rustls/README.md @@ -0,0 +1,12 @@ +# rustls example with strict sni and strict alpn + +Tested with rustls 0.20.0 + +based on https://github.com/rustls/rustls/blob/main/rustls-mio/examples/ + +needs tls-baseimage already in docker + + +```bash +./run.sh +``` \ No newline at end of file diff --git a/evaluation-libraries/rustls/build.sh b/evaluation-libraries/rustls/build.sh new file mode 100755 index 0000000..f8d3a32 --- /dev/null +++ b/evaluation-libraries/rustls/build.sh @@ -0,0 +1 @@ +docker build --build-arg VERSION=0.20.0 . -t tls-rustls -f Dockerfile \ No newline at end of file diff --git a/evaluation-libraries/rustls/client/client.rs b/evaluation-libraries/rustls/client/client.rs new file mode 100644 index 0000000..a038ae5 --- /dev/null +++ b/evaluation-libraries/rustls/client/client.rs @@ -0,0 +1,412 @@ +extern crate clap; +use clap::{Arg, App}; + +use std::sync::Arc; + +use mio; +use mio::net::{TcpStream}; +use std::fs; +use std::io; +use std::io::{BufReader, Read, Write}; + +use std::process; +use std::collections; +use rustls; +use rustls::{RootCertStore}; +use std::sync::Mutex; +use std::convert::TryFrom; + +const CLIENT: mio::Token = mio::Token(0); + +/// This encapsulates the TCP-level connection, some connection +/// state, and the underlying TLS-level session. +struct TlsClient { + socket: TcpStream, + closing: bool, + clean_closure: bool, + tls_conn: rustls::ClientConnection, +} + +impl TlsClient { + fn new( + sock: TcpStream, + server_name: rustls::ServerName, + cfg: Arc, + ) -> TlsClient { + TlsClient { + socket: sock, + closing: false, + clean_closure: false, + tls_conn: rustls::ClientConnection::new(cfg, server_name).unwrap(), + } + } + + /// Handles events sent to the TlsClient by mio::Poll + fn ready(&mut self, ev: &mio::event::Event) { + assert_eq!(ev.token(), CLIENT); + + if ev.is_readable() { + self.do_read(); + } + + if ev.is_writable() { + self.do_write(); + } + + if self.is_closed() { + println!("Connection closed"); + process::exit(if self.clean_closure { 0 } else { 1 }); + } + } + + /// We're ready to do a read. + fn do_read(&mut self) { + // Read TLS data. This fails if the underlying TCP connection + // is broken. + match self.tls_conn.read_tls(&mut self.socket) { + Err(error) => { + if error.kind() == io::ErrorKind::WouldBlock { + return; + } + println!("TLS read error: {:?}", error); + self.closing = true; + return; + } + + // If we're ready but there's no data: EOF. + Ok(0) => { + println!("EOF"); + self.closing = true; + self.clean_closure = true; + return; + } + + Ok(_) => {} + }; + + // Reading some TLS data might have yielded new TLS + // messages to process. Errors from this indicate + // TLS protocol problems and are fatal. + let io_state = match self.tls_conn.process_new_packets() { + Ok(io_state) => io_state, + Err(err) => { + println!("TLS error: {:?}", err); + self.closing = true; + return; + } + }; + + // Having read some TLS data, and processed any new messages, + // we might have new plaintext as a result. + // + // Read it and then write it to stdout. + if io_state.plaintext_bytes_to_read() > 0 { + let mut plaintext = Vec::new(); + plaintext.resize(io_state.plaintext_bytes_to_read(), 0u8); + self.tls_conn + .reader() + .read(&mut plaintext) + .unwrap(); + io::stdout() + .write_all(&plaintext) + .unwrap(); + } + + // If wethat fails, the peer might have started a clean TLS-level + // session closure. + if io_state.peer_has_closed() { + self.clean_closure = true; + self.closing = true; + return; + } + } + + fn do_write(&mut self) { + self.tls_conn + .write_tls(&mut self.socket) + .unwrap(); + } + + /// Registers self as a 'listener' in mio::Registry + fn register(&mut self, registry: &mio::Registry) { + let interest = self.event_set(); + registry + .register(&mut self.socket, CLIENT, interest) + .unwrap(); + } + + /// Reregisters self as a 'listener' in mio::Registry. + fn reregister(&mut self, registry: &mio::Registry) { + let interest = self.event_set(); + registry + .reregister(&mut self.socket, CLIENT, interest) + .unwrap(); + } + + /// Use wants_read/wants_write to register for different mio-level + /// IO readiness events. + fn event_set(&self) -> mio::Interest { + let rd = self.tls_conn.wants_read(); + let wr = self.tls_conn.wants_write(); + + if rd && wr { + mio::Interest::READABLE | mio::Interest::WRITABLE + } else if wr { + mio::Interest::WRITABLE + } else { + mio::Interest::READABLE + } + } + + fn is_closed(&self) -> bool { + self.closing + } +} +impl io::Write for TlsClient { + fn write(&mut self, bytes: &[u8]) -> io::Result { + self.tls_conn.writer().write(bytes) + } + + fn flush(&mut self) -> io::Result<()> { + self.tls_conn.writer().flush() + } +} + +impl io::Read for TlsClient { + fn read(&mut self, bytes: &mut [u8]) -> io::Result { + self.tls_conn.reader().read(bytes) + } +} + +/// This is an example cache for client session data. +/// It optionally dumps cached data to a file, but otherwise +/// is just in-memory. +/// +/// Note that the contents of such a file are extremely sensitive. +/// Don't write this stuff to disk in production code. +struct PersistCache { + cache: Mutex, Vec>>, + filename: Option, +} + +impl PersistCache { + /// Make a new cache. If filename is Some, load the cache + /// from it and flush changes back to that file. + fn _new(filename: &Option) -> Self { + let cache = PersistCache { + cache: Mutex::new(collections::HashMap::new()), + filename: filename.clone(), + }; + if cache.filename.is_some() { + cache.load(); + } + cache + } + + /// If we have a filename, save the cache contents to it. + fn save(&self) { + use rustls::internal::msgs::base::PayloadU16; + use rustls::internal::msgs::codec::Codec; + + if self.filename.is_none() { + return; + } + + let mut file = + fs::File::create(self.filename.as_ref().unwrap()).expect("cannot open cache file"); + + for (key, val) in self.cache.lock().unwrap().iter() { + let mut item = Vec::new(); + let key_pl = PayloadU16::new(key.clone()); + let val_pl = PayloadU16::new(val.clone()); + key_pl.encode(&mut item); + val_pl.encode(&mut item); + file.write_all(&item).unwrap(); + } + } + + /// We have a filename, so replace the cache contents from it. + fn load(&self) { + use rustls::internal::msgs::base::PayloadU16; + use rustls::internal::msgs::codec::{Codec, Reader}; + + let mut file = match fs::File::open(self.filename.as_ref().unwrap()) { + Ok(f) => f, + Err(_) => return, + }; + let mut data = Vec::new(); + file.read_to_end(&mut data).unwrap(); + + let mut cache = self.cache.lock().unwrap(); + cache.clear(); + let mut rd = Reader::init(&data); + + while rd.any_left() { + let key_pl = PayloadU16::read(&mut rd).unwrap(); + let val_pl = PayloadU16::read(&mut rd).unwrap(); + cache.insert(key_pl.0, val_pl.0); + } + } +} + +impl rustls::client::StoresClientSessions for PersistCache { + /// put: insert into in-memory cache, and perhaps persist to disk. + fn put(&self, key: Vec, value: Vec) -> bool { + self.cache + .lock() + .unwrap() + .insert(key, value); + self.save(); + true + } + + /// get: from in-memory cache + fn get(&self, key: &[u8]) -> Option> { + self.cache + .lock() + .unwrap() + .get(key) + .cloned() + } +} + +// TODO: um, well, it turns out that openssl s_client/s_server +// that we use for testing doesn't do ipv6. So we can't actually +// test ipv6 and hence kill this. +fn lookup_ipv4(host: &str, port: u16) -> std::net::SocketAddr { + use std::net::ToSocketAddrs; + + let addrs = (host, port).to_socket_addrs().unwrap(); + for addr in addrs { + if let std::net::SocketAddr::V4(_) = addr { + return addr; + } + } + + unreachable!("Cannot lookup address"); +} + +#[cfg(feature = "dangerous_configuration")] +mod danger { + use super::rustls; + + pub struct NoCertificateVerification {} + + impl rustls::client::ServerCertVerifier for NoCertificateVerification { + fn verify_server_cert( + &self, + _end_entity: &rustls::Certificate, + _intermediates: &[rustls::Certificate], + _server_name: &rustls::ServerName, + _scts: &mut dyn Iterator, + _ocsp: &[u8], + _now: std::time::SystemTime, + ) -> Result { + Ok(rustls::client::ServerCertVerified::assertion()) + } + } +} + +// #[cfg(feature = "dangerous_configuration")] +// fn apply_dangerous_options(args: &Args, cfg: &mut rustls::ClientConfig) { +// if args.flag_insecure { +// cfg.dangerous() +// .set_certificate_verifier(Arc::new(danger::NoCertificateVerification {})); +// } +// } + +// #[cfg(not(feature = "dangerous_configuration"))] +// fn apply_dangerous_options(args: &Args, _: &mut rustls::ClientConfig) { +// if args.flag_insecure { +// panic!("This build does not support --insecure."); +// } +// } + +/// Parse some arguments, then make a TLS client connection +/// somewhere. +fn main() { + let matches = App::new("alpaca-rustls") + .version("1.0") + .arg(Arg::with_name("cert") + .short("c") + .long("cert") + .takes_value(true)) + .arg(Arg::with_name("host") + .short("h") + .long("host") + .takes_value(true)) + .arg(Arg::with_name("servername") + .short("s") + .long("servername") + .takes_value(true)) + .arg(Arg::with_name("alpn") + .short("a") + .long("alpn") + .takes_value(true)) + .get_matches(); + + let cert = matches.value_of("cert").unwrap_or("/etc/ssl/cert-data/ca.crt"); + let host = matches.value_of("host").unwrap_or("127.0.0.1"); + let servername = matches.value_of("servername").unwrap_or("tls-server.com"); + let alpn = matches.value_of("alpn").unwrap_or("http/1.1"); + + println!("Parameters alpn={} servername={} cert={} host={} port={} ", alpn, servername, cert, host, "4433"); + + let addr = lookup_ipv4(host, 4433); + + let mut root_store = RootCertStore::empty(); + let certfile = fs::File::open(&cert).expect("Cannot open CA file"); + let mut reader = BufReader::new(certfile); + root_store.add_parsable_certificates(&rustls_pemfile::certs(&mut reader).unwrap()); + + let suites = rustls::ALL_CIPHER_SUITES.to_vec(); + let versions = rustls::DEFAULT_VERSIONS.to_vec(); + + let mut config = rustls::ClientConfig::builder() + .with_cipher_suites(&suites) + .with_safe_default_kx_groups() + .with_protocol_versions(&versions) + .expect("inconsistent cipher-suite/versions selected") + .with_root_certificates(root_store) + .with_no_client_auth(); + + config.key_log = Arc::new(rustls::KeyLogFile::new()); + + //config.enable_sni = false; + + config.alpn_protocols = vec![alpn.as_bytes().to_vec()]; + //config.max_fragment_size = args.flag_max_frag_size; + + //apply_dangerous_options(args, &mut config); + + let test = Arc::new(config); + + + let sock = TcpStream::connect(addr).unwrap(); + let dnslookup = rustls::ServerName::try_from(servername).expect("invalid DNS name"); + + // let server_name = args + // .arg_hostname + // .as_str() + // .try_into() + // .expect("invalid DNS name"); + let mut tlsclient = TlsClient::new(sock, dnslookup, test); + + let message = format!("Hello from Client!\n"); + tlsclient + .write_all(message.as_bytes()) + .unwrap(); + + let mut poll = mio::Poll::new().unwrap(); + let mut events = mio::Events::with_capacity(32); + tlsclient.register(poll.registry()); + + loop { + poll.poll(&mut events, None).unwrap(); + + for ev in events.iter() { + tlsclient.ready(&ev); + tlsclient.reregister(poll.registry()); + } + } +} \ No newline at end of file diff --git a/evaluation-libraries/rustls/docker-compose.yml b/evaluation-libraries/rustls/docker-compose.yml new file mode 100644 index 0000000..a61bf80 --- /dev/null +++ b/evaluation-libraries/rustls/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + rustls-server: + image: tls-rustls + openssl-server-wrong-cn: + image: tls-openssl + command: [ "/openssl-server", "-k", "/etc/ssl/cert-data/wrong-cn.com.key", "-c" , "/etc/ssl/cert-data/wrong-cn.com-chain.crt"] + openssl-malicious-alpn: + image: tls-openssl + command: [ "/openssl-server", "-m"] + rustls-client: + image: tls-rustls + command: [ "./client.sh", "/client", "rustls-server", "openssl-server-wrong-cn", "openssl-malicious-alpn" ,"1"] + depends_on: + - rustls-server + - openssl-server-wrong-cn + - openssl-malicious-alpn diff --git a/evaluation-libraries/rustls/run.sh b/evaluation-libraries/rustls/run.sh new file mode 100755 index 0000000..b09b619 --- /dev/null +++ b/evaluation-libraries/rustls/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --abort-on-container-exit --exit-code-from rustls-client --remove-orphans diff --git a/evaluation-libraries/rustls/server/server.rs b/evaluation-libraries/rustls/server/server.rs new file mode 100644 index 0000000..284870f --- /dev/null +++ b/evaluation-libraries/rustls/server/server.rs @@ -0,0 +1,463 @@ +extern crate clap; +use clap::{Arg, App}; + +use std::sync::Arc; + +use mio; +use mio::net::{TcpListener, TcpStream}; +use std::collections::HashMap; + +use std::fs; +use std::io; +use std::io::{BufReader, Read, Write}; +use std::net; + + +use rustls; + +// Token for our listening socket. +const LISTENER: mio::Token = mio::Token(0); + +/// This binds together a TCP listening socket, some outstanding +/// connections, and a TLS server configuration. +struct TlsServer { + server: TcpListener, + connections: HashMap, + next_id: usize, + tls_config: Arc, +} + +impl TlsServer { + fn new(server: TcpListener, cfg: Arc) -> Self { + TlsServer { + server, + connections: HashMap::new(), + next_id: 2, + tls_config: cfg, + } + } + + fn accept(&mut self, registry: &mio::Registry) -> Result<(), io::Error> { + loop { + match self.server.accept() { + Ok((socket, addr)) => { + println!("Accepting new connection from {:?}", addr); + + let tls_conn = + rustls::ServerConnection::new(Arc::clone(&self.tls_config)).unwrap(); + + let token = mio::Token(self.next_id); + self.next_id += 1; + + let mut connection = OpenConnection::new(socket, token, tls_conn); + + connection.register(registry); + self.connections + .insert(token, connection); + } + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => return Ok(()), + Err(err) => { + println!( + "encountered error while accepting connection; err={:?}", + err + ); + return Err(err); + } + } + } + } + + fn conn_event(&mut self, registry: &mio::Registry, event: &mio::event::Event) { + let token = event.token(); + + if self.connections.contains_key(&token) { + self.connections + .get_mut(&token) + .unwrap() + .ready(registry, event); + + if self.connections[&token].is_closed() { + self.connections.remove(&token); + } + } + } +} + +/// This is a connection which has been accepted by the server, +/// and is currently being served. +/// +/// It has a TCP-level stream, a TLS-level connection state, and some +/// other state/metadata. +struct OpenConnection { + socket: TcpStream, + token: mio::Token, + closing: bool, + closed: bool, + tls_conn: rustls::ServerConnection, + back: Option, +} + +/// Open a plaintext TCP-level connection for forwarded connections. +fn open_back() -> Option { + None +} + +/// This used to be conveniently exposed by mio: map EWOULDBLOCK +/// errors to something less-errory. +fn try_read(r: io::Result) -> io::Result> { + match r { + Ok(len) => Ok(Some(len)), + Err(e) => { + if e.kind() == io::ErrorKind::WouldBlock { + Ok(None) + } else { + Err(e) + } + } + } +} + +impl OpenConnection { + fn new( + socket: TcpStream, + token: mio::Token, + tls_conn: rustls::ServerConnection, + ) -> OpenConnection { + let back = open_back(); + OpenConnection { + socket, + token, + closing: false, + closed: false, + tls_conn, + back, + } + } + + /// We're a connection, and we have something to do. + fn ready(&mut self, registry: &mio::Registry, ev: &mio::event::Event) { + + let servername = rustls::ServerConnection::sni_hostname(&self.tls_conn); + + if servername.is_some() { + if servername.unwrap().eq("tls-server.com") { + println!("SNI :{}", servername.unwrap()); + } + else { + println!("INVALID SNI :{}", servername.unwrap()); + self.tls_conn.send_close_notify(); + let _ = self + .socket + .shutdown(net::Shutdown::Both); + self.close_back(); + self.closed = true; + //self.deregister(registry); + } + } + + // If we're readable: read some TLS. Then + // see if that yielded new plaintext. Then + // see if the backend is readable too. + if ev.is_readable() { + self.do_tls_read(); + self.try_plain_read(); + self.try_back_read(); + } + + if ev.is_writable() { + self.do_tls_write_and_handle_error(); + } + + if self.closing { + let _ = self + .socket + .shutdown(net::Shutdown::Both); + self.close_back(); + self.closed = true; + self.deregister(registry); + } else { + self.reregister(registry); + } + } + + // Close the backend connection for forwarded sessions. + fn close_back(&mut self) { + if self.back.is_some() { + let back = self.back.as_mut().unwrap(); + back.shutdown(net::Shutdown::Both) + .unwrap(); + } + self.back = None; + } + + fn do_tls_read(&mut self) { + // Read some TLS data. + match self.tls_conn.read_tls(&mut self.socket) { + Err(err) => { + if let io::ErrorKind::WouldBlock = err.kind() { + return; + } + + println!("read error {:?}", err); + self.closing = true; + return; + } + Ok(0) => { + println!("eof"); + self.closing = true; + return; + } + Ok(_) => {} + }; + + // Process newly-received TLS messages. + if let Err(err) = self.tls_conn.process_new_packets() { + println!("cannot process packet: {:?}", err); + + // last gasp write to send any alerts + self.do_tls_write_and_handle_error(); + + self.closing = true; + } + } + + fn try_plain_read(&mut self) { + // Read and process all available plaintext. + if let Ok(io_state) = self.tls_conn.process_new_packets() { + if io_state.plaintext_bytes_to_read() > 0 { + let mut buf = Vec::new(); + buf.resize(io_state.plaintext_bytes_to_read(), 0u8); + + self.tls_conn + .reader() + .read(&mut buf) + .unwrap(); + + println!("plaintext read {:?}", buf.len()); + self.incoming_plaintext(&buf); + } + } + } + + fn try_back_read(&mut self) { + if self.back.is_none() { + return; + } + + // Try a non-blocking read. + let mut buf = [0u8; 1024]; + let back = self.back.as_mut().unwrap(); + let rc = try_read(back.read(&mut buf)); + + if rc.is_err() { + println!("backend read failed: {:?}", rc); + self.closing = true; + return; + } + + let maybe_len = rc.unwrap(); + + // If we have a successful but empty read, that's an EOF. + // Otherwise, we shove the data into the TLS session. + match maybe_len { + Some(len) if len == 0 => { + println!("back eof"); + self.closing = true; + } + Some(len) => { + self.tls_conn + .writer() + .write_all(&buf[..len]) + .unwrap(); + } + None => {} + }; + } + + /// Process some amount of received plaintext. + fn incoming_plaintext(&mut self, _buf: &[u8]) { + // self.tls_conn + // .writer() + // .write_all(buf) + // .unwrap(); + + let response = b"Hello from Server!\n"; + self.tls_conn + .writer() + .write_all(response) + .unwrap(); + self.tls_conn.send_close_notify(); + } + + fn tls_write(&mut self) -> io::Result { + self.tls_conn.write_tls(&mut self.socket) + } + + fn do_tls_write_and_handle_error(&mut self) { + let rc = self.tls_write(); + if rc.is_err() { + println!("write failed {:?}", rc); + self.closing = true; + return; + } + } + + fn register(&mut self, registry: &mio::Registry) { + let event_set = self.event_set(); + registry + .register(&mut self.socket, self.token, event_set) + .unwrap(); + + if self.back.is_some() { + registry + .register( + self.back.as_mut().unwrap(), + self.token, + mio::Interest::READABLE, + ) + .unwrap(); + } + } + + fn reregister(&mut self, registry: &mio::Registry) { + let event_set = self.event_set(); + registry + .reregister(&mut self.socket, self.token, event_set) + .unwrap(); + } + + fn deregister(&mut self, registry: &mio::Registry) { + registry + .deregister(&mut self.socket) + .unwrap(); + + if self.back.is_some() { + registry + .deregister(self.back.as_mut().unwrap()) + .unwrap(); + } + } + + /// What IO events we're currently waiting for, + /// based on wants_read/wants_write. + fn event_set(&self) -> mio::Interest { + let rd = self.tls_conn.wants_read(); + let wr = self.tls_conn.wants_write(); + + if rd && wr { + mio::Interest::READABLE | mio::Interest::WRITABLE + } else if wr { + mio::Interest::WRITABLE + } else { + mio::Interest::READABLE + } + } + + fn is_closed(&self) -> bool { + self.closed + } +} + +fn load_certs(filename: &str) -> Vec { + let certfile = fs::File::open(filename).expect("cannot open certificate file"); + let mut reader = BufReader::new(certfile); + rustls_pemfile::certs(&mut reader) + .unwrap() + .iter() + .map(|v| rustls::Certificate(v.clone())) + .collect() +} + +fn load_private_key(filename: &str) -> rustls::PrivateKey { + let keyfile = fs::File::open(filename).expect("cannot open private key file"); + let mut reader = BufReader::new(keyfile); + + loop { + match rustls_pemfile::read_one(&mut reader).expect("cannot parse private key .pem file") { + Some(rustls_pemfile::Item::RSAKey(key)) => return rustls::PrivateKey(key), + Some(rustls_pemfile::Item::PKCS8Key(key)) => return rustls::PrivateKey(key), + None => break, + _ => {} + } + } + + panic!( + "no keys found in {:?} (encrypted keys not supported)", + filename + ); +} + +fn main() { + let matches = App::new("alpaca-rustls") + .version("1.0") + .author("tls-server author") + .arg(Arg::with_name("cert") + .short("c") + .long("cert") + .takes_value(true)) + .arg(Arg::with_name("key") + .short("k") + .long("key") + .takes_value(true)) + .get_matches(); + + let cert = matches.value_of("cert").unwrap_or("/etc/ssl/cert-data/tls-server.com-chain.crt"); + let key = matches.value_of("key").unwrap_or("/etc/ssl/cert-data/tls-server.com.key"); + + let addr: net::SocketAddr = "0.0.0.0:4433".parse().unwrap(); + + let client_auth = rustls::server::NoClientAuth::new(); + let suites = rustls::ALL_CIPHER_SUITES.to_vec(); + let versions = rustls::ALL_VERSIONS.to_vec(); + + let certs = load_certs(cert); + let privkey = load_private_key(key); + + let mut config = rustls::ServerConfig::builder() + .with_cipher_suites(&suites) + .with_safe_default_kx_groups() + .with_protocol_versions(&versions) + .expect("inconsistent cipher-suites/versions specified") + .with_client_cert_verifier(client_auth) + .with_single_cert(certs, privkey) + .expect("bad certificates/private key"); + + config.key_log = Arc::new(rustls::KeyLogFile::new()); + + // let protocols = Vec::new(); + // protocols.push("http/1.1"); + let protocols = "http/1.1"; + config.alpn_protocols = vec![protocols.as_bytes().to_vec()]; + + // config.alpn_protocols = args + // .flag_a + // .iter() + // .map(|a| a.as_bytes().to_vec()) + // .collect::>(); + + let test = Arc::new(config); + + let mut listener = TcpListener::bind(addr).expect("cannot listen on port"); + let mut poll = mio::Poll::new().unwrap(); + poll.registry() + .register(&mut listener, LISTENER, mio::Interest::READABLE) + .unwrap(); + + let mut tlsserv = TlsServer::new(listener, test); + + let mut events = mio::Events::with_capacity(256); + loop { + poll.poll(&mut events, None).unwrap(); + + for event in events.iter() { + match event.token() { + LISTENER => { + tlsserv + .accept(poll.registry()) + .expect("error accepting socket"); + } + _ => tlsserv.conn_event(poll.registry(), &event), + } + } + } +} \ No newline at end of file diff --git a/evaluation-libraries/wolfssl/CMakeLists.txt b/evaluation-libraries/wolfssl/CMakeLists.txt new file mode 100644 index 0000000..90eadb4 --- /dev/null +++ b/evaluation-libraries/wolfssl/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.0.0) +project(alpaca-wolfssl VERSION 0.1.0) + +add_subdirectory(client) +add_subdirectory(server) diff --git a/evaluation-libraries/wolfssl/Dockerfile b/evaluation-libraries/wolfssl/Dockerfile new file mode 100644 index 0000000..14384f9 --- /dev/null +++ b/evaluation-libraries/wolfssl/Dockerfile @@ -0,0 +1,19 @@ +# syntax=docker/dockerfile:1 +FROM tls-baseimage as tls-wolfssl +ARG VERSION=4.8.1-stable +WORKDIR /build +RUN git clone --depth=1 --branch=v${VERSION} https://github.com/wolfSSL/wolfssl +WORKDIR /build/wolfssl +RUN ./autogen.sh +RUN ./configure --prefix=/build/ --enable-static --enable-sni --enable-alpn +RUN make +WORKDIR /build +ADD server /build/server +ADD client /build/client +ADD CMakeLists.txt /build/CMakeLists.txt +RUN cmake . .. && make +RUN mv /build/server/server / +RUN mv /build/client/client / +COPY --from=tls-openssl /openssl-client /openssl-client +WORKDIR / +CMD ["/server"] diff --git a/evaluation-libraries/wolfssl/LICENSE b/evaluation-libraries/wolfssl/LICENSE new file mode 100644 index 0000000..5393a81 --- /dev/null +++ b/evaluation-libraries/wolfssl/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. \ No newline at end of file diff --git a/evaluation-libraries/wolfssl/README.md b/evaluation-libraries/wolfssl/README.md new file mode 100644 index 0000000..6567fdf --- /dev/null +++ b/evaluation-libraries/wolfssl/README.md @@ -0,0 +1,12 @@ +# wolfssl example with strict sni and strict alpn + +Tested with wolfssl 4.8.1-stable + +Based on https://github.com/wolfSSL/wolfssl-examples + +needs tls-baseimage already in docker + + +```bash +./run.sh +``` \ No newline at end of file diff --git a/evaluation-libraries/wolfssl/build.sh b/evaluation-libraries/wolfssl/build.sh new file mode 100755 index 0000000..368679d --- /dev/null +++ b/evaluation-libraries/wolfssl/build.sh @@ -0,0 +1 @@ +docker build --build-arg VERSION=4.8.1-stable . -t tls-wolfssl -f Dockerfile \ No newline at end of file diff --git a/evaluation-libraries/wolfssl/client/CMakeLists.txt b/evaluation-libraries/wolfssl/client/CMakeLists.txt new file mode 100644 index 0000000..a033c3e --- /dev/null +++ b/evaluation-libraries/wolfssl/client/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.0.0) +project(client VERSION 0.1.0) + +add_library(wolfssl STATIC IMPORTED) +set_target_properties(wolfssl PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/wolfssl/src/.libs/libwolfssl.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/wolfssl" + ) +add_executable(client client.c) +target_link_libraries(client wolfssl m) +target_compile_options(client PRIVATE -Wall -Wextra) diff --git a/evaluation-libraries/wolfssl/client/client.c b/evaluation-libraries/wolfssl/client/client.c new file mode 100644 index 0000000..57db3ca --- /dev/null +++ b/evaluation-libraries/wolfssl/client/client.c @@ -0,0 +1,182 @@ +/* client-tls.c + * + * Copyright (C) 2006-2020 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include +#include +#include + +/* wolfSSL */ +#include +#include + +#include "client.h" + +//#define HAVE_ALPN +//#define HAVE_SNI + +int main(int argc, char **argv) { + char *host = "localhost"; + char *servername = "tls-server.com"; + char *port = "4433"; + char *alpn = "http/1.1"; + char *cert = "/etc/ssl/certs/ca.crt"; + + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:c:h:p")) != -1) { + switch (opt) { + case 'a': + alpn = optarg; + break; + case 's': + servername = optarg; + break; + case 'h': + host = optarg; + break; + case 'p': + port = optarg; + break; + case 'c': + cert = optarg; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-h ip] [-p port] [-c certificate] \n", argv[0]); + exit(EXIT_FAILURE); + } + } + printf("Parameters alpn=%s servername=%s cert=%s host=%s port=%s \n", alpn, servername, cert, host, port); + + int sockfd; + char buff[256]; + int len; + int ret; + + /* declare wolfSSL objects */ + WOLFSSL_CTX *ctx; + WOLFSSL *ssl; + + sockfd = tcp_connect(host, port); + + /* Initialize wolfSSL */ + if ((ret = wolfSSL_Init()) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: Failed to initialize the library\n"); + goto socket_cleanup; + } + + /* Create and initialize WOLFSSL_CTX */ + if ((ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())) == NULL) { + fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n"); + ret = -1; + goto socket_cleanup; + } + + /* Load client certificates into WOLFSSL_CTX */ + if ((ret = wolfSSL_CTX_load_verify_locations(ctx, cert, NULL)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", + cert); + goto ctx_cleanup; + } + + /* Create a WOLFSSL object */ + if ((ssl = wolfSSL_new(ctx)) == NULL) { + fprintf(stderr, "ERROR: failed to create WOLFSSL object\n"); + ret = -1; + goto ctx_cleanup; + } + + /* Attach wolfSSL to the socket */ + if ((ret = wolfSSL_set_fd(ssl, sockfd)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: Failed to set the file descriptor\n"); + goto cleanup; + } + + /* set ALPN */ + if (wolfSSL_UseALPN(ssl, alpn, sizeof(alpn), WOLFSSL_ALPN_FAILED_ON_MISMATCH) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to set ALPN \n"); + goto cleanup; + } + + /* set SNI */ + ret = wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, servername, strlen(servername)); + if (ret != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to set SNI \n"); + goto cleanup; + } + + /* hostname verification */ + ret = wolfSSL_check_domain_name(ssl, servername); + if (ret != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: setting hostname verification \n"); + goto cleanup; + } + + /* Connect to wolfSSL on the server side */ + if ((ret = wolfSSL_connect(ssl)) != WOLFSSL_SUCCESS) { + char buffer[WOLFSSL_MAX_ERROR_SZ]; + int err = wolfSSL_get_error(ssl, ret); + char *error_string = wolfSSL_ERR_error_string(err, buffer); + fprintf(stderr, "wolfSSL_connect error = %s\n", error_string); + if (strstr(error_string, "Unrecognized protocol name Error") != NULL) { + ret = 120; + } + if (strstr(error_string, "peer subject name mismatch") != NULL) { + ret = 42; + } + goto cleanup; + } + + /* Send message to server */ + char *message = "Hello from Client!\n"; + strcpy(buff, message); + len = strlen(message); + if ((ret = wolfSSL_write(ssl, buff, len)) != len) { + fprintf(stderr, "ERROR: failed to write entire message\n"); + fprintf(stderr, "%d bytes of %d bytes were sent", ret, (int)len); + goto cleanup; + } + + /* Read the server data into our buff array */ + memset(buff, 0, sizeof(buff)); + if ((ret = wolfSSL_read(ssl, buff, sizeof(buff) - 1)) == -1) { + fprintf(stderr, "ERROR: failed to read\n"); + goto cleanup; + } + printf("%s\n", buff); + + /* Bidirectional shutdown */ + while (wolfSSL_shutdown(ssl) == SSL_SHUTDOWN_NOT_DONE) { + //printf("Shutdown not complete\n"); + } + + printf("Connection closed.\n"); + + ret = 0; + + /* Cleanup and return */ +cleanup: + wolfSSL_free(ssl); /* Free the wolfSSL object */ +ctx_cleanup: + wolfSSL_CTX_free(ctx); /* Free the wolfSSL context object */ + wolfSSL_Cleanup(); /* Cleanup the wolfSSL environment */ +socket_cleanup: + close(sockfd); /* Close the connection to the server */ + return ret; /* Return reporting a success */ +} \ No newline at end of file diff --git a/evaluation-libraries/wolfssl/client/client.h b/evaluation-libraries/wolfssl/client/client.h new file mode 100644 index 0000000..8aa1d7a --- /dev/null +++ b/evaluation-libraries/wolfssl/client/client.h @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int tcp_connect(const char *hostname, const char *port) { + int sockfd, portno; + struct sockaddr_in serv_addr; + struct hostent *server; + portno = atoi(port); + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) + printf("ERROR opening socket"); + server = gethostbyname(hostname); + if (server == NULL) { + fprintf(stderr, "ERROR, no such host\n"); + exit(0); + } + bzero((char *)&serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, + (char *)&serv_addr.sin_addr.s_addr, + server->h_length); + serv_addr.sin_port = htons(portno); + int err = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + if (err < 0) { + fprintf(stderr, "Connect error\n"); + exit(1); + } + return sockfd; +} \ No newline at end of file diff --git a/evaluation-libraries/wolfssl/docker-compose.yml b/evaluation-libraries/wolfssl/docker-compose.yml new file mode 100644 index 0000000..a6790ef --- /dev/null +++ b/evaluation-libraries/wolfssl/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + wolfssl-server: + image: tls-wolfssl + openssl-server-wrong-cn: + image: tls-openssl + command: [ "/openssl-server", "-k", "/etc/ssl/cert-data/wrong-cn.com.key", "-c" , "/etc/ssl/cert-data/wrong-cn.com-chain.crt"] + openssl-malicious-alpn: + image: tls-openssl + command: [ "/openssl-server", "-m"] + wolfssl-client: + image: tls-wolfssl + command: [ "./client.sh", "/client", "wolfssl-server", "openssl-server-wrong-cn", "openssl-malicious-alpn" ,"1"] + depends_on: + - wolfssl-server + - openssl-server-wrong-cn + - openssl-malicious-alpn diff --git a/evaluation-libraries/wolfssl/run.sh b/evaluation-libraries/wolfssl/run.sh new file mode 100755 index 0000000..b43d9c4 --- /dev/null +++ b/evaluation-libraries/wolfssl/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --abort-on-container-exit --exit-code-from wolfssl-client --remove-orphans diff --git a/evaluation-libraries/wolfssl/server/CMakeLists.txt b/evaluation-libraries/wolfssl/server/CMakeLists.txt new file mode 100644 index 0000000..714b088 --- /dev/null +++ b/evaluation-libraries/wolfssl/server/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.0.0) +project(server VERSION 0.1.0) + +add_library(wolfssl STATIC IMPORTED) +set_target_properties(wolfssl PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/wolfssl/src/.libs/libwolfssl.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/wolfssl" + ) +add_executable(server server.c) +target_link_libraries(server wolfssl m) +target_compile_options(server PRIVATE -Wall -Wextra) diff --git a/evaluation-libraries/wolfssl/server/server.c b/evaluation-libraries/wolfssl/server/server.c new file mode 100644 index 0000000..1597ed7 --- /dev/null +++ b/evaluation-libraries/wolfssl/server/server.c @@ -0,0 +1,199 @@ +/* server-tls.c + * + * Copyright (C) 2006-2020 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "server.h" + +#include +#include +#include + +/* wolfSSL */ +#include +#include + +int main(int argc, char **argv) { + char *servername = "tls-server.com"; + uint16_t port = 4433; + char *alpn = "http/1.1"; + char *cert = "/etc/ssl/cert-data/tls-server.com-chain.crt"; + char *key = "/etc/ssl/cert-data/tls-server.com.key"; + + int sockfd = SOCKET_INVALID; + int connd = SOCKET_INVALID; + struct sockaddr_in clientAddr; + socklen_t size = sizeof(clientAddr); + char buff[256]; + int len; + int ret; + const char *reply = "Hello from Server!\n"; + + /* Get commandline arguments */ + int opt; + while ((opt = getopt(argc, argv, "a:s:c:k:p")) != -1) { + switch (opt) { + case 'a': + alpn = optarg; + break; + case 's': + servername = optarg; + break; + case 'k': + key = optarg; + break; + case 'p': + port = strtol(optarg, NULL, 10); + break; + case 'c': + cert = optarg; + break; + default: + fprintf(stderr, "Usage: %s [-a alpn] [-s servername] [-k keyfile] [-p port] [-c certificate] \n", argv[0]); + exit(EXIT_FAILURE); + } + } + printf("Parameters alpn=%s servername=%s cert=%s key=%s port=%d \n", alpn, servername, cert, key, port); + + /* declare wolfSSL objects */ + WOLFSSL_CTX *ctx = NULL; + WOLFSSL *ssl = NULL; + + /* Initialize wolfSSL */ + wolfSSL_Init(); + + /* Create and initialize WOLFSSL_CTX */ + if ((ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())) == NULL) { + fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n"); + ret = -1; + goto exit; + } + + /* Load server certificates into WOLFSSL_CTX */ + if ((ret = wolfSSL_CTX_use_certificate_chain_file(ctx, cert)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", cert); + goto exit; + } + + /* Load server key into WOLFSSL_CTX */ + if ((ret = wolfSSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", key); + goto exit; + } + + /* set SNI */ + ret = wolfSSL_CTX_UseSNI(ctx, WOLFSSL_SNI_HOST_NAME, servername, strlen(servername)); + if (ret != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to set SNI \n"); + goto exit; + } + + sockfd = create_socket(port); + listen(sockfd, 1024); + + for (;;) { + fprintf(stderr, "Waiting for a connection...\n"); + + /* Accept client connections */ + if ((connd = accept(sockfd, (struct sockaddr *)&clientAddr, &size)) == -1) { + fprintf(stderr, "ERROR: failed to accept the connection\n"); + continue; + } + + /* Create a WOLFSSL object */ + if ((ssl = wolfSSL_new(ctx)) == NULL) { + fprintf(stderr, "ERROR: failed to create WOLFSSL object\n"); + continue; + } + + /* set ALPN */ + if (wolfSSL_UseALPN(ssl, alpn, sizeof(alpn), WOLFSSL_ALPN_FAILED_ON_MISMATCH) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to set ALPN \n"); + wolfSSL_shutdown(ssl); + continue; + } + + /* Attach wolfSSL to the socket */ + wolfSSL_set_fd(ssl, connd); + + /* Establish TLS connection */ + ret = wolfSSL_accept(ssl); + if (ret != WOLFSSL_SUCCESS) { + char buffer[WOLFSSL_MAX_ERROR_SZ]; + fprintf(stderr, "wolfSSL_accept error = %s\n", wolfSSL_ERR_error_string(wolfSSL_get_error(ssl, ret), buffer)); + wolfSSL_shutdown(ssl); + continue; + } + + //fprintf(stderr, "Client connected successfully \n"); + + /* Read the client data into our buff array */ + memset(buff, 0, sizeof(buff)); + if ((ret = wolfSSL_read(ssl, buff, sizeof(buff) - 1)) == -1) { + fprintf(stderr, "ERROR: failed to read\n"); + wolfSSL_shutdown(ssl); + continue; + } + + /* Print to stdout any data the client sends */ + fprintf(stderr, "%s\n", buff); + + /* Check for server shutdown command */ + if (strncmp(buff, "shutdown", 8) == 0) { + printf("Shutdown command issued!\n"); + wolfSSL_shutdown(ssl); + continue; + } + + /* Write our reply into buff */ + memset(buff, 0, sizeof(buff)); + memcpy(buff, reply, strlen(reply)); + len = strnlen(buff, sizeof(buff)); + + /* Reply back to the client */ + if ((ret = wolfSSL_write(ssl, buff, len)) != len) { + fprintf(stderr, "ERROR: failed to write\n"); + wolfSSL_shutdown(ssl); + continue; + } + + /* Notify the client that the connection is ending */ + wolfSSL_shutdown(ssl); + fprintf(stderr, "Connection closed.\n"); + + /* Cleanup after this connection */ + wolfSSL_free(ssl); /* Free the wolfSSL object */ + close(connd); /* Close the connection to the client */ + } + + ret = 0; +exit: + /* Cleanup and return */ + if (ssl) + wolfSSL_free(ssl); /* Free the wolfSSL object */ + if (connd != SOCKET_INVALID) + close(connd); /* Close the connection to the client */ + if (sockfd != SOCKET_INVALID) + close(sockfd); /* Close the socket listening for clients */ + if (ctx) + wolfSSL_CTX_free(ctx); /* Free the wolfSSL context object */ + wolfSSL_Cleanup(); /* Cleanup the wolfSSL environment */ + + return ret; /* Return reporting a success */ +} \ No newline at end of file diff --git a/evaluation-libraries/wolfssl/server/server.h b/evaluation-libraries/wolfssl/server/server.h new file mode 100644 index 0000000..5ceeae3 --- /dev/null +++ b/evaluation-libraries/wolfssl/server/server.h @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +int create_socket(uint16_t port) { + int listen_sd; + struct sockaddr_in sa_serv; + int optval = 1; + /* Socket operations */ + listen_sd = socket(AF_INET, SOCK_STREAM, 0); + + memset(&sa_serv, '\0', sizeof(sa_serv)); + sa_serv.sin_family = AF_INET; + sa_serv.sin_addr.s_addr = INADDR_ANY; + sa_serv.sin_port = htons(port); + + setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int)); + + bind(listen_sd, (struct sockaddr *)&sa_serv, sizeof(sa_serv)); + + return listen_sd; +} diff --git a/evaluation-servers/.gitignore b/evaluation-servers/.gitignore new file mode 100644 index 0000000..6b1c132 --- /dev/null +++ b/evaluation-servers/.gitignore @@ -0,0 +1,4 @@ +*tar.gz +httpd-* +nginx-* +sendmail-* \ No newline at end of file diff --git a/evaluation-servers/README.md b/evaluation-servers/README.md new file mode 100644 index 0000000..41ace9f --- /dev/null +++ b/evaluation-servers/README.md @@ -0,0 +1,35 @@ +# alpaca-server +Testing TLS Servers ALPN and SNI Implementation. + +Scans each Server with TLS-Scanner https://github.com/tls-attacker/TLS-Scanner + +## Requirements +- docker and docker-compose +- baseimage Docker containers from evaluation-libraries + +---------------- +## Running Servers +1. Build the baseimage from evaluation-libraries. +2. Build the TLS-Scanner container with ``./build.sh`` +3. Go into any subdirectory and do ``./run.sh`` + +## Server overview and versions tested +'strict' means the server rejects the connection if he doesn't recognize the ALPN or SNI sent. +| Server | ALPN |SNI | +| ------------- | ------------- | ------------- | +| apache 2.4.51 | not strict | not strict | +| nginx 1.21.4 | strict | not strict | +| lighttpd 1.4.63 | strict | not strict | +| postfix/smtpd 3.6.2 | ------------ | not strict | +| openSMTPD 6.8.0 | ------------ | ------------ | +| sendmail 8.17.1 | ------------ | ------------ | +| exim 4.95 | strict | ------------ | +| Courier 5.10 | strict | not strict | +| Dovecot 2.3.13 | ------------ | not strict | +| pure-ftpd 1.0.49 | ------------ | not strict | +| cyrus 3.4.2-1 | strict in master only https | not strict | +| ProFTPD 1.3.8rc2 | ------------ | strict | +| vsftpd 3.0.5 | strict | strict | +| filezilla server 1.1.0 | strict | not strict | + + diff --git a/evaluation-servers/apache/Dockerfile b/evaluation-servers/apache/Dockerfile new file mode 100644 index 0000000..1e63ce7 --- /dev/null +++ b/evaluation-servers/apache/Dockerfile @@ -0,0 +1,5 @@ +FROM httpd:2.4.51 as tls-apache +COPY ./apache.conf /usr/local/apache2/conf/httpd.conf +COPY --from=tls-baseimage /etc/ssl/cert-data/tls-server.com-chain.crt /usr/local/apache2/conf/server-chain.crt +COPY --from=tls-baseimage /etc/ssl/cert-data/tls-server.com.crt /usr/local/apache2/conf/server.crt +COPY --from=tls-baseimage /etc/ssl/cert-data/tls-server.com.key /usr/local/apache2/conf/server.key \ No newline at end of file diff --git a/evaluation-servers/apache/apache.conf b/evaluation-servers/apache/apache.conf new file mode 100644 index 0000000..d1629cd --- /dev/null +++ b/evaluation-servers/apache/apache.conf @@ -0,0 +1,45 @@ +LoadModule socache_shmcb_module modules/mod_socache_shmcb.so +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule reqtimeout_module modules/mod_reqtimeout.so +LoadModule filter_module modules/mod_filter.so +LoadModule mime_module modules/mod_mime.so +LoadModule env_module modules/mod_env.so +LoadModule setenvif_module modules/mod_setenvif.so +LoadModule version_module modules/mod_version.so +LoadModule unixd_module modules/mod_unixd.so +LoadModule headers_module modules/mod_headers.so +LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule ssl_module modules/mod_ssl.so +LoadModule status_module modules/mod_status.so +LoadModule dir_module modules/mod_dir.so +LoadModule alias_module modules/mod_alias.so +LoadModule autoindex_module modules/mod_autoindex.so +LoadModule http2_module modules/mod_http2.so +Include conf/extra/httpd-ssl.conf + + +User daemon +Group daemon + + +ServerRoot "/usr/local/apache2" +ServerName tls-server:4433 + +SSLProtocol +TLSv1.2 +TLSv1.3 + +Listen 4433 + + Protocols h2 http/1.1 + DocumentRoot "/usr/local/apache2/htdocs" + ServerName tls-server + SSLEngine on + SSLCertificateFile /usr/local/apache2/conf/server.crt + SSLCertificateKeyFile /usr/local/apache2/conf/server.key + SSLCertificateChainFile /usr/local/apache2/conf/server-chain.crt + diff --git a/evaluation-servers/apache/build.sh b/evaluation-servers/apache/build.sh new file mode 100755 index 0000000..6178687 --- /dev/null +++ b/evaluation-servers/apache/build.sh @@ -0,0 +1 @@ +docker build . -t tls-apache diff --git a/evaluation-servers/apache/docker-compose.yml b/evaluation-servers/apache/docker-compose.yml new file mode 100644 index 0000000..ae5a710 --- /dev/null +++ b/evaluation-servers/apache/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + httpd: + image: tls-apache + scanner: + image: tlsscanner + depends_on: + - httpd + command: [ "-connect", "httpd:4433", "-server_name", "tls-server.com", "-scanDetail", "QUICK"] diff --git a/evaluation-servers/apache/run.sh b/evaluation-servers/apache/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/apache/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/build.sh b/evaluation-servers/build.sh new file mode 100755 index 0000000..318af17 --- /dev/null +++ b/evaluation-servers/build.sh @@ -0,0 +1,4 @@ +git clone https://github.com/tls-attacker/TLS-Scanner.git +cd TLS-Scanner +git submodule update --init --recursive +docker build . -t tlsscanner \ No newline at end of file diff --git a/evaluation-servers/courier/Dockerfile b/evaluation-servers/courier/Dockerfile new file mode 100644 index 0000000..1698580 --- /dev/null +++ b/evaluation-servers/courier/Dockerfile @@ -0,0 +1,40 @@ +FROM tls-baseimage-archlinux + +# Avoid ERROR: invoke-rc.d: policy-rc.d denied execution of start. +#RUN echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d + +RUN pacman -Syu --noconfirm + +USER build + +## Version 1.1.5-2 + +WORKDIR /src/ +RUN git clone https://aur.archlinux.org/courier-unicode.git +WORKDIR /src/courier-unicode +RUN git checkout b08066fde2b4147076cb3201888fc2ee68eed19c +RUN makepkg -si --noconfirm + +WORKDIR /src/ +RUN git clone https://aur.archlinux.org/courier-authlib.git +WORKDIR /src/courier-authlib +RUN git checkout 125c9823c551500428857a503f2d4a3b795aa589 +RUN makepkg -si --noconfirm + +WORKDIR /src/ +RUN git clone https://aur.archlinux.org/courier-mta.git +WORKDIR /src/courier-mta +RUN git checkout 359ca3946091a4634d1c6aab60df2e079cdde08 +RUN makepkg -si --noconfirm + +USER root +#ARG DEBIAN_FRONTEND=noninteractive +#RUN apt-get update && apt-get install -yq courier-imap +RUN cp /etc/ssl/cert-data/tls-server.com-chain.crt /etc/courier/imapd.cert +RUN cp /etc/ssl/cert-data/tls-server.com-chain.crt /etc/courier/pop3d.cert +RUN cp /etc/ssl/cert-data/tls-server.com.key /etc/courier/imapd.key +RUN cp /etc/ssl/cert-data/tls-server.com.key /etc/courier/pop3d.key +ADD start.sh /root/ +ADD imapd-ssl /etc/courier/ +RUN chmod +x /root/start.sh +CMD ["/root/start.sh"] \ No newline at end of file diff --git a/evaluation-servers/courier/build.sh b/evaluation-servers/courier/build.sh new file mode 100755 index 0000000..a4c7ae1 --- /dev/null +++ b/evaluation-servers/courier/build.sh @@ -0,0 +1,2 @@ +docker build . -t tls-courier + diff --git a/evaluation-servers/courier/docker-compose.yml b/evaluation-servers/courier/docker-compose.yml new file mode 100644 index 0000000..e932164 --- /dev/null +++ b/evaluation-servers/courier/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + courier: + image: tls-courier + scanner: + image: tlsscanner + depends_on: + - courier + command: [ "-connect", "courier:993", "-server_name", "tls-server.com", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/courier/imapd-ssl b/evaluation-servers/courier/imapd-ssl new file mode 100644 index 0000000..f53ef69 --- /dev/null +++ b/evaluation-servers/courier/imapd-ssl @@ -0,0 +1,331 @@ +##VERSION: $Id: f6b3b133f021939f6a08728c42dd07a35cf2c8fc-20210515145926$ +# +# imapd-ssl created from imapd-ssl.dist by sysconftool +# +# Do not alter lines that begin with ##, they are used when upgrading +# this configuration. +# +# Copyright 2000 - 2021 Double Precision, Inc. See COPYING for +# distribution information. +# +# This configuration file sets various options for the Courier-IMAP server +# when used to handle SSL IMAP connections. +# +# SSL and non-SSL connections are handled by a dedicated instance of the +# couriertcpd daemon. If you are accepting both SSL and non-SSL IMAP +# connections, you will start two instances of couriertcpd, one on the +# IMAP port 143, and another one on the IMAP-SSL port 993. +# +# Download OpenSSL from http://www.openssl.org/ +# +##NAME: SSLPORT:1 +# +# Options in the imapd-ssl configuration file AUGMENT the options in the +# imapd configuration file. First the imapd configuration file is read, +# then the imapd-ssl configuration file, so we do not have to redefine +# anything. +# +# However, some things do have to be redefined. The port number is +# specified by SSLPORT, instead of PORT. The default port is port 993. +# +# Multiple port numbers can be separated by commas. When multiple port +# numbers are used it is possibly to select a specific IP address for a +# given port as "ip.port". For example, "127.0.0.1.900,192.168.0.1.900" +# accepts connections on port 900 on IP addresses 127.0.0.1 and 192.168.0.1 +# The SSLADDRESS setting is a default for ports that do not have +# a specified IP address. + +SSLPORT=993 + +##NAME: SSLADDRESS:0 +# +# Address to listen on, can be set to a single IP address. +# +# SSLADDRESS=127.0.0.1 + +SSLADDRESS=0 + +##NAME: SSLPIDFILE:0 +# +# That's the SSL IMAP port we'll listen on. +# Feel free to redefine MAXDAEMONS, TCPDOPTS, and MAXPERIP. + +SSLPIDFILE=/run/courier/imapd-ssl.pid + +##NAME: SSLLOGGEROPTS:0 +# +# courierlogger(1) options. +# + +SSLLOGGEROPTS="-name=imapd-ssl" + +##NAME: IMAPDSSLSTART:0 +# +# Different pid files, so that both instances of couriertcpd can coexist +# happily. +# +# You can also redefine IMAP_CAPABILITY, although I can't +# think of why you'd want to do that. +# +# +# Ok, the following settings are new to imapd-ssl: +# +# Whether or not to start IMAP over SSL on simap port: + +IMAPDSSLSTART=NO + +##NAME: IMAPDSTARTTLS:0 +# +# Whether or not to implement IMAP STARTTLS extension instead: + +IMAPDSTARTTLS=YES + +##NAME: IMAP_TLS_REQUIRED:1 +# +# Set IMAP_TLS_REQUIRED to 1 if you REQUIRE STARTTLS for everyone. +# (this option advertises the LOGINDISABLED IMAP capability, until STARTTLS +# is issued). + +IMAP_TLS_REQUIRED=1 + + +######################################################################### +# +# The following variables configure IMAP over SSL. If OpenSSL or GnuTLS +# is available during configuration, the couriertls helper gets compiled, and +# upon installation a dummy TLS_CERTFILE gets generated. +# +# WARNING: Peer certificate verification has NOT yet been tested. Proceed +# at your own risk. Only the basic SSL/TLS functionality is known to be +# working. Keep this in mind as you play with the following variables. +# +##NAME: COURIERTLS:0 +# + +COURIERTLS=/usr/bin/couriertls + +##NAME: TLS_PRIORITY:0 +# +# GnuTLS setting only (use TLS_CIPHER_LIST for OpenSSL) +# +# Set TLS protocol priority settings +# +# DEFAULT: NORMAL:-CTYPE-OPENPGP +# +# This setting is also used to select the available ciphers. +# +# The actual list of available ciphers depend on the options GnuTLS was +# compiled against. The possible ciphers are: +# +# AES256, 3DES, AES128, ARC128, ARC40, RC2, DES, NULL +# +# Also, the following aliases: +# +# HIGH -- all ciphers that use more than a 128 bit key size +# MEDIUM -- all ciphers that use a 128 bit key size +# LOW -- all ciphers that use fewer than a 128 bit key size, the NULL cipher +# is not included +# ALL -- all ciphers except the NULL cipher +# +TLS_PRIORITY="NORMAL:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:+VERS-TLS1.2" +# See GnuTLS documentation, gnutls_priority_init(3) for additional +# documentation. + +##NAME: TLS_PROTOCOL:0 +# +# TLS_PROTOCOL sets the protocol version. The possible versions are: +# +# OpenSSL: +# +# TLSv1 - TLS 1.0, or higher. +# TLSv1.1 - TLS1.1, or higher. +# TLSv1.1++ TLS1.1, or higher, without client-initiated renegotiation. +# TLSv1.2 - TLS1.2, or higher. +# TLSv1.2++ TLS1.2, or higher, without client-initiated renegotiation. +# +# The default value is TLSv1 + +##NAME: TLS_CIPHER_LIST:0 +# +# OpenSSL only (use TLS_PRIORITY for GnuTLS): +# +# TLS_CIPHER_LIST optionally sets the list of ciphers to be used by the +# OpenSSL library. In most situations you can leave TLS_CIPHER_LIST +# undefined +# +# TLS_CIPHER_LIST="TLSv1:HIGH:!LOW:!MEDIUM:!EXP:!NULL:!aNULL@STRENGTH" +# +# See the OpenSSL ciphers(1) manual page for the format of this setting. + +##NAME: TLS_STARTTLS_PROTOCOL:0 +# +# TLS_STARTTLS_PROTOCOL is used instead of TLS_PROTOCOL for the IMAP STARTTLS +# extension, as opposed to IMAP over SSL on port 993. +# +# It takes the same values for OpenSSL as TLS_PROTOCOL +TLS_PROTOCOL="TLSv1.2" +TLS_STARTTLS_PROTOCOL="TLSv1.2" + +##NAME: TLS_MIN_DH_BITS:0 +# +# TLS_MIN_DH_BITS=n +# +# GnuTLS only: +# +# Set the minimum number of acceptable bits for a DH key exchange. +# +# GnuTLS's compiled-in default is 727 bits (as of GnuTLS 1.6.3). Some server +# have been encountered that offer 512 bit keys. You may have to set +# TLS_MIN_DH_BITS=512 here, if necessary. + +##NAME: TLS_TIMEOUT:0 +# TLS_TIMEOUT is currently not implemented, and reserved for future use. +# This is supposed to be an inactivity timeout, but its not yet implemented. +# + +##NAME: TLS_CERTFILE:0 +# +# TLS_CERTFILE - certificate to use. TLS_CERTFILE must be owned +# by the "courier" user, and must not be world-readable. +# +# VIRTUAL HOSTS ON THE SAME IP ADDRESS. +# +# Install each certificate $TLS_CERTFILE.domain, so if TLS_CERTFILE is set to +# /etc/certificate.pem, then you'll need to install the actual certificate +# files as /etc/certificate.pem.www.example.com, +# /etc/certificate.pem.www.domain.com and so on. Then, create a link from +# $TLS_CERTFILE to whichever certificate you consider to be the main one, +# for example: +# /etc/certificate.pem => /etc/certificate.pem.www.example.com +# +# IP-BASED VIRTUAL HOSTS: +# +# There may be a need to support older SSL/TLS client that don't support +# virtual hosts on the same IP address, and require a dedicated IP address +# for each SSL/TLS host. If so, install each certificate file as +# $TLS_CERTFILE.aaa.bbb.ccc.ddd, where "aaa.bbb.ccc.ddd" is the IP address +# for the certificate's domain name. So, if TLS_CERTFILE is set to +# /etc/certificate.pem, then you'll need to install the actual certificate +# files as /etc/certificate.pem.192.168.0.2, /etc/certificate.pem.192.168.0.3 +# and so on, for each IP address. +# +# In all cases, $TLS_CERTFILE needs to be linked to one of the existing +# certificate files. + +TLS_CERTFILE=/etc/ssl/cert-data/tls-server-chain.crt + +##NAME: TLS_PRIVATE_KEYFILE:0 +# +# TLS_PRIVATE_KEYFILE - SSL/TLS private key for decrypting peer data. +# This file must be owned by the "courier" user, and must not be world +# readable, and must be accessible without a pass-phrase, i.e. it must not +# be encrypted. +# +# By default, courier generates SSL/TLS certifice including private key +# and install it in TLS_CERTFILE path, so TLS_PRIVATE_KEYFILE is completely +# optional. If TLS_PRIVATE_KEYFILE is not set (default), TLS_CERTFILE is +# treated as certificate including private key file. +# +# If you get SSL/TLS certificate and private key from trusted certificate +# authority(CA) and want to install them separately, TLS_PRIVATE_KEYFILE can +# be used as private key file path setting. +# +# VIRTUAL HOSTS ON THE SAME IP ADDRESS. +# +# $TLS_PRIVATE_KEYFILE.domain and $TLS_CERTFILE.domain are a pair. +# If you use VIRTUAL HOST feature on TLS_CERTFILE setting, you must set pair +# private key as $TLS_PRIVATE_KEYFILE.domain. Then, create a link from +# $TLS_PRIVATE_KEYFILE to whichever private key you consider to be the main one. +# for example: +# /etc/tls_private_keyfile.pem => /etc/tls_private_keyfile.pem.www.example.com +# +# IP-BASED VIRTUAL HOSTS: +# +# Just described on "VIRTUAL HOSTS ON THE SAME IP ADDRESS" above, +# $TLS_PRIVATE_KEYFILE.aaa.bbb.ccc.ddd and $TLS_CERTFILE.aaa.bbb.ccc.ddd are +# a pair. If TLS_PRIVATE_KEYFILE is set to /etc/tls_private_keyfile.pem, +# then you'll need to install the actual certificate files as +# /etc/tls_private_keyfile.pem.192.168.0.2, /etc/tls_private_keyfile.192.168.0.3 +# and so on, for each IP address. +# +# In all cases, $TLS_PRIVATE_KEYFILE needs to be linked to one of the existing +# certificate files. +# +TLS_PRIVATE_KEYFILE=/etc/ssl/cert-data/tls-server.key + +##NAME: TLS_DHPARAMS:0 +# +# TLS_DHPARAMS - DH parameter file. +# +TLS_DHPARAMS=/usr/share/dhparams.pem + +##NAME: TLS_TRUSTCERTS:0 +# +# TLS_TRUSTCERTS=pathname - load trusted certificates from pathname. +# pathname can be a file or a directory. If a file, the file should +# contain a list of trusted certificates, in PEM format. If a +# directory, the directory should contain the trusted certificates, +# in PEM format, one per file and hashed using OpenSSL's c_rehash +# script. TLS_TRUSTCERTS is used by SSL/TLS clients (by specifying +# the -domain option) and by SSL/TLS servers (TLS_VERIFYPEER is set +# to PEER or REQUIREPEER). +# + +TLS_TRUSTCERTS=/etc/ssl/certs/ + +##NAME: TLS_VERIFYPEER:0 +# +# TLS_VERIFYPEER - how to verify client certificates. The possible values of +# this setting are: +# +# NONE - do not verify anything +# +# PEER - verify the client certificate, if one's presented +# +# REQUIREPEER - require a client certificate, fail if one's not presented +# +# +TLS_VERIFYPEER=NONE + +##NAME: TLS_EXTERNAL:0 +# +# To enable SSL certificate-based authentication: +# +# 1) TLS_TRUSTCERTS must be set to a pathname that holds your certificate +# authority's SSL certificate +# +# 2) TLS_VERIFYPEER=PEER or TLS_VERIFYPEER=REQUIREPEER (the later settings +# requires all SSL clients to present a certificate, and rejects +# SSL/TLS connections without a valid cert). +# +# 3) Set TLS_EXTERNAL, below, to the subject field that holds the login ID. +# Example: +# +# TLS_EXTERNAL=emailaddress +# +# The above example retrieves the login ID from the "emailaddress" subject +# field. The certificate's emailaddress subject must match exactly the login +# ID in the courier-authlib database. + +##NAME: TLS_CACHE:1 +# +# A TLS/SSL session cache may slightly improve response for IMAP clients +# that open multiple SSL sessions to the server. TLS_CACHEFILE will be +# automatically created, TLS_CACHESIZE bytes long, and used as a cache +# buffer. + +TLS_CACHEFILE=/var/spool/courier/couriersslimapcache +TLS_CACHESIZE=524288 + +##NAME: TLS_ALPN:0 +# +# Application level protocol negotiation should be enabled by default, and +# should be commented out only in case of compatibility issues. + +TLS_ALPN=imap + +##NAME: MAILDIRPATH:0 +# +# MAILDIRPATH - directory name of the maildir directory. +# +MAILDIRPATH=Maildir \ No newline at end of file diff --git a/evaluation-servers/courier/run.sh b/evaluation-servers/courier/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/courier/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/courier/smtpd.conf b/evaluation-servers/courier/smtpd.conf new file mode 100644 index 0000000..2a9e45e --- /dev/null +++ b/evaluation-servers/courier/smtpd.conf @@ -0,0 +1,26 @@ +# $OpenBSD: smtpd.conf,v 1.10 2018/05/24 11:40:17 gilles Exp $ + +# This is the smtpd server system-wide configuration file. +# See smtpd.conf(5) for more information. + +table aliases file:/etc/aliases + +pki tls-server.com cert "/etc/ssl/cert-data/tls-server.com-chain.crt" +pki tls-server.com key "/etc/ssl/cert-data/tls-server.com.key" + +# To accept external mail, replace with: listen on all +# +#listen on 0.0.0.0 smtps + +listen on 0.0.0.0 tls pki tls-server.com +listen on 0.0.0.0 port 465 smtps pki tls-server.com +listen on 0.0.0.0 port 587 tls-require pki tls-server.com + +action "local" maildir alias +action "relay" relay + +# Uncomment the following to accept external mail for domain "example.org" +# +# match from any for domain "example.org" action "local" +match for local action "local" +match from local for any action "relay" \ No newline at end of file diff --git a/evaluation-servers/courier/start.sh b/evaluation-servers/courier/start.sh new file mode 100644 index 0000000..80fa888 --- /dev/null +++ b/evaluation-servers/courier/start.sh @@ -0,0 +1,9 @@ +#!/bin/bash +mkdir -p /run/courier/authdaemon +touch /run/courier/authdaemon/pid.lock +touch /run/courier/imapd-ssl.pid.lock +makeimapaccess +/usr/sbin/authdaemond start +/usr/sbin/imapd start +/usr/sbin/imapd-ssl start +while true; do sleep 1000; done \ No newline at end of file diff --git a/evaluation-servers/cyrus/Dockerfile b/evaluation-servers/cyrus/Dockerfile new file mode 100644 index 0000000..70ed1d4 --- /dev/null +++ b/evaluation-servers/cyrus/Dockerfile @@ -0,0 +1,40 @@ +FROM tls-baseimage-archlinux + +RUN pacman -Syu --noconfirm + +USER build + +#Fixed to Version 3.4.2 + +WORKDIR /src/ +RUN git clone https://aur.archlinux.org/perl-pod-pom.git +WORKDIR /src/perl-pod-pom +RUN git checkout 2699d4b77c2fb0573b1445968afaca4aa36299d4 +RUN makepkg -si --noconfirm + +WORKDIR /src/ +RUN git clone https://aur.archlinux.org/perl-pod-pom-view-restructured.git +WORKDIR /src/perl-pod-pom-view-restructured +RUN git checkout 80138742fbb711e3a56b4018ce4dcc7f0ec1b212 +RUN makepkg -si --noconfirm + +WORKDIR /src/ +RUN git clone https://aur.archlinux.org/cyrus-imapd.git +WORKDIR /src/cyrus-imapd +RUN git checkout a1e53ee172fc37431080dd3ff3685ffd2a2378e5 +#remove pgp key from PKGBUILD +RUN sed -i '/^validpgpkeys/d' PKGBUILD +RUN sed -i 's/{,.sig}//g' PKGBUILD +RUN sed -i '/'SKIP'/d' PKGBUILD +RUN makepkg -si --noconfirm + +USER root + +RUN echo "tls-server" >> /etc/hostname +RUN echo "tls-server 127.0.0.1" >> /etc/hosts + +ADD cyrus.conf /etc/cyrus/cyrus.conf +ADD imapd.conf /etc/cyrus/imapd.conf + +CMD ["/usr/lib/cyrus/master"] + diff --git a/evaluation-servers/cyrus/build.sh b/evaluation-servers/cyrus/build.sh new file mode 100755 index 0000000..4591b31 --- /dev/null +++ b/evaluation-servers/cyrus/build.sh @@ -0,0 +1,2 @@ +docker build . -t tls-cyrus + diff --git a/evaluation-servers/cyrus/cyrus.asc b/evaluation-servers/cyrus/cyrus.asc new file mode 100644 index 0000000..378b95b --- /dev/null +++ b/evaluation-servers/cyrus/cyrus.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFU5pZUBCAC+m05W9nJnBkrfFO9I+iimF1WCsSZNFoASJ3WEeZxIkOQO9BZj +aKf8EP/nK7nEfNGZ2m+OrAtQU/+I8Sk1ppHuwZgENLvRzLsBGbv80kDKBw31Nd1f +sCpVQs4b8zlohXjq0UN8tT5NcGJnGE7ahoOHzJk/0Ll76oVmOZvSw+WHBp1945m2 +Q8CbIbfmyuv7NF6GtGDVilPeIPsDnh5w5usjpKsxjYHKpy6Rtf4MbcCLtkRbHFra +KJD+xum0PgPdCAEEbQsSXQgwOd0TZ59avRVVef674PjWqIuudUGUhJ/f9OWOj7LG +6QgJR6yvCy7Bc2eAN4RnIIzaUZGaJDKDCNozABEBAAG0ImVsbGllIHRpbW9uZXkg +PGVsbGllQGZhc3RtYWlsLmNvbT6JATgEEwECACIFAlU5pZUCGwMGCwkIBwMCBhUI +AgkKCwQWAgMBAh4BAheAAAoJEFVPBP6zY3jgb9gH/3GPDLGybo7SYZMtBmfe+Udf +tcRkTtH+o2pf2rh6KwPhhEDuOXWVCIUPWXsWIVU2K5Y8AdBIHOEoSUp3n8juV57I +u9CfDI718/WaHgEpYrq5DqyROAFr+sGahcb6C40+V/CeUSAmKVhFGniuALUSAQ+B +XVj/i2EAFNg/5ALkPYDnDYDqm7Ak6odDbktYQz987y38sg3EMC/2wi2EoOG1VWeG +twFD8HKmXZw+u6cYtFh9K1hOBZm+PhLHr3h1MHTuWYeBKkT3YqaGtXMwi704LlNr +HU8beOHSNBSsVYJ61B4kgBA7p+qnx6xIpU2KfAJl8cgjCYwrq8yo+Lm9TazagfM= +=dIwC +-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/evaluation-servers/cyrus/cyrus.conf b/evaluation-servers/cyrus/cyrus.conf new file mode 100644 index 0000000..629c2f4 --- /dev/null +++ b/evaluation-servers/cyrus/cyrus.conf @@ -0,0 +1,55 @@ +# standard standalone server implementation + +START { + # do not delete this entry! + recover cmd="ctl_cyrusdb -r" +} + +# UNIX sockets start with a slash and are put into /run/cyrus/socket +SERVICES { + # add or remove based on preferences + imap cmd="imapd" listen="imap" prefork=0 + imaps cmd="imapd -s" listen="imaps" prefork=0 + #pop3 cmd="pop3d" listen="pop3" prefork=0 + #pop3s cmd="pop3d -s" listen="pop3s" prefork=0 + sieve cmd="timsieved" listen="sieve" prefork=0 + + # these are only necessary if receiving/exporting usenet via NNTP +# nntp cmd="nntpd" listen="nntp" prefork=0 +# nntps cmd="nntpd -s" listen="nntps" prefork=0 + + # these are only necessary if using HTTP for CalDAV, CardDAV, or RSS + #http cmd="httpd" listen="http" prefork=0 + https cmd="httpd -s" listen="https" prefork=0 + + # at least one LMTP is required for delivery +# lmtp cmd="lmtpd" listen="lmtp" prefork=0 + lmtpunix cmd="lmtpd" listen="/run/cyrus/socket/lmtp" prefork=0 + + # this is requied if using socketmap +# smmap cmd="smmapd" listen="/run/cyrus/socket/smmap" prefork=0 + + # this is required if using notifications +# notify cmd="notifyd" listen="/run/cyrus/socket/notify" proto="udp" prefork=1 +} + +EVENTS { + # this is required + checkpoint cmd="ctl_cyrusdb -c" period=30 + + # this is only necessary if using duplicate delivery suppression, + # Sieve or NNTP + delprune cmd="cyr_expire -E 3" at=0400 + + # Expire data older than 28 days. + deleteprune cmd="cyr_expire -E 4 -D 28" at=0430 + expungeprune cmd="cyr_expire -E 4 -X 28" at=0445 + + # this is only necessary if caching TLS sessions + tlsprune cmd="tls_prune" at=0400 +} + +DAEMON { + # this is only necessary if using idled for IMAP IDLE +# idled cmd="idled" +} \ No newline at end of file diff --git a/evaluation-servers/cyrus/docker-compose.yml b/evaluation-servers/cyrus/docker-compose.yml new file mode 100644 index 0000000..33f17c2 --- /dev/null +++ b/evaluation-servers/cyrus/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + cyrus: + image: tls-cyrus + scanner: + image: tlsscanner + depends_on: + - cyrus + command: [ "-connect", "cyrus:993", "-server_name", "tls-server.com", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/cyrus/imapd.conf b/evaluation-servers/cyrus/imapd.conf new file mode 100644 index 0000000..88cea46 --- /dev/null +++ b/evaluation-servers/cyrus/imapd.conf @@ -0,0 +1,130 @@ +# Suggested minimal imapd.conf +# See imapd.conf(5) for more information and more options + +# Space-separated users who have admin rights for all services. +# NB: THIS MUST BE CONFIGURED +admins: cyrus + +################################################################### +## File, socket and DB location settings. +################################################################### + +# Configuration directory +configdirectory: /var/lib/cyrus + +# Directories for proc and lock files +proc_path: /run/cyrus/proc +mboxname_lockpath: /run/cyrus/lock + +# Locations for DB files +# The following DB are recreated upon initialization, so should live in +# ephemeral storage for best performance. +duplicate_db_path: /run/cyrus/deliver.db +ptscache_db_path: /run/cyrus/ptscache.db +statuscache_db_path: /run/cyrus/statuscache.db +tls_sessions_db_path: /run/cyrus/tls_sessions.db + +# Which partition to use for default mailboxes +defaultpartition: default +partition-default: /var/spool/cyrus/mail + +# If sieveusehomedir is false (the default), this directory is searched +# for Sieve scripts. +sievedir: /var/spool/sieve + +################################################################### +## Important: KEEP THESE IN SYNC WITH cyrus.conf +################################################################### + +lmtpsocket: /run/cyrus/socket/lmtp +idlesocket: /run/cyrus/socket/idle +notifysocket: /run/cyrus/socket/notify + +# Syslog prefix. Defaults to cyrus (so logging is done as cyrus/imap +# etc.) +syslog_prefix: cyrus + +################################################################### +## Server behaviour settings +################################################################### + +# Space-separated list of HTTP modules that will be enabled in +# httpd(8). This option has no effect on modules that are disabled at +# compile time due to missing dependencies (e.g. libical). +# +# Allowed values: caldav, carddav, domainkey, ischedule, rss +httpmodules: caldav carddav + +# If enabled, the partitions will also be hashed, in addition to the +# hashing done on configuration directories. This is recommended if one +# partition has a very bushy mailbox tree. +hashimapspool: true + +# Enable virtual domains +# and set default domain to localhost +virtdomains: yes +defaultdomain: tls-server + +# Use these credentials to run services +cyrus_user: cyrus +cyrus_group: mail + +################################################################### +## User experience settings +################################################################### + +# Minimum time between POP mail fetches in minutes +popminpoll: 1 + +################################################################### +## User Authentication settings +################################################################### + +# Allow plaintext logins by default (SASL PLAIN) +allowplaintext: no + +################################################################### +## SASL library options (these are handled directly by the SASL +## libraries, refer to SASL documentation for an up-to-date list of +## these) +################################################################### + +# The mechanism(s) used by the server to verify plaintext passwords. +# Possible values are "saslauthd", "auxprop", "pwcheck" and +# "alwaystrue". They are tried in order, you can specify more than one, +# separated by spaces. +sasl_pwcheck_method: saslauthd + +# If enabled, the SASL library will automatically create authentication +# secrets when given a plaintext password. Refer to SASL documentation +sasl_auto_transition: no + +################################################################### +## SSL/TLS Options +################################################################### + +# File containing the global certificate used for ALL services (imap, +# pop3, lmtp, sieve) +tls_server_cert: /etc/ssl/cert-data/tls-server-chain.crt + +# File containing the private key belonging to the global server +# certificate. +tls_server_key: /etc/ssl/cert-data/tls-server.key + + +# File containing one or more Certificate Authority (CA) certificates. +#tls_client_ca_file: /etc/ssl/certs/cyrus-imapd-ca.pem + +# Path to directory with certificates of CAs. +tls_client_ca_dir: /etc/ssl/certs + +# The length of time (in minutes) that a TLS session will be cached for +# later reuse. The maximum value is 1440 (24 hours), the default. A +# value of 0 will disable session caching. +tls_session_timeout: 1440 + +tls_ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 +tls_required: 1 +tls_prefer_server_ciphers: 1 +tls_versions: tls1_2 tls1_3 +servername: tls-server diff --git a/evaluation-servers/cyrus/run.sh b/evaluation-servers/cyrus/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/cyrus/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/dovecot/Dockerfile b/evaluation-servers/dovecot/Dockerfile new file mode 100644 index 0000000..1ae1bcc --- /dev/null +++ b/evaluation-servers/dovecot/Dockerfile @@ -0,0 +1,6 @@ +FROM tls-baseimagedebian +RUN apt-get update && apt-get install -y dovecot-imapd dovecot-pop3d +RUN cp /etc/ssl/cert-data/tls-server.com-chain.crt /etc/dovecot/private/dovecot.pem +RUN cp /etc/ssl/cert-data/tls-server.com.key /etc/dovecot/private/dovecot.key +RUN echo "ssl_min_protocol = TLSv1.2" >> /etc/dovecot/conf.d/10-ssl.conf +CMD ["dovecot", "-F"] \ No newline at end of file diff --git a/evaluation-servers/dovecot/build.sh b/evaluation-servers/dovecot/build.sh new file mode 100755 index 0000000..961bca6 --- /dev/null +++ b/evaluation-servers/dovecot/build.sh @@ -0,0 +1 @@ +docker build . -t tls-dovecot diff --git a/evaluation-servers/dovecot/docker-compose.yml b/evaluation-servers/dovecot/docker-compose.yml new file mode 100644 index 0000000..1051543 --- /dev/null +++ b/evaluation-servers/dovecot/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + dovecot: + image: tls-dovecot + scanner: + image: tlsscanner + depends_on: + - dovecot + command: [ "-connect", "dovecot:993", "-server_name", "tls-server.com", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/dovecot/run.sh b/evaluation-servers/dovecot/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/dovecot/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/exim/Dockerfile b/evaluation-servers/exim/Dockerfile new file mode 100644 index 0000000..46dc188 --- /dev/null +++ b/evaluation-servers/exim/Dockerfile @@ -0,0 +1,11 @@ +FROM tls-baseimage +RUN apk add exim +ADD exim.conf /etc/exim/exim.conf + +RUN chmod 777 /etc/ssl/cert-data/tls-server.com-chain.crt +RUN chmod 777 /etc/ssl/cert-data/tls-server.com.key + +USER exim +#CMD ["exim", "-bd", "-d-all+pid", "-q30m"] +ENTRYPOINT ["exim"] +CMD ["-bd", "-v", "-oP", "/dev/null"] \ No newline at end of file diff --git a/evaluation-servers/exim/build.sh b/evaluation-servers/exim/build.sh new file mode 100755 index 0000000..95e654c --- /dev/null +++ b/evaluation-servers/exim/build.sh @@ -0,0 +1 @@ +docker build . -t tls-exim diff --git a/evaluation-servers/exim/docker-compose.yml b/evaluation-servers/exim/docker-compose.yml new file mode 100644 index 0000000..d85b793 --- /dev/null +++ b/evaluation-servers/exim/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + exim: + image: tls-exim + hostname: tls-exim + scanner: + image: tlsscanner + depends_on: + - exim + command: [ "-connect", "exim:465", "-server_name", "tls-server.com", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/exim/exim.conf b/evaluation-servers/exim/exim.conf new file mode 100644 index 0000000..52bc199 --- /dev/null +++ b/evaluation-servers/exim/exim.conf @@ -0,0 +1,1034 @@ +###################################################################### +# Runtime configuration file for Exim # +###################################################################### + + +# This is a default configuration file which will operate correctly in +# uncomplicated installations. Please see the manual for a complete list +# of all the runtime configuration options that can be included in a +# configuration file. There are many more than are mentioned here. The +# manual is in the file doc/spec.txt in the Exim distribution as a plain +# ASCII file. Other formats (PostScript, Texinfo, HTML, PDF) are available +# from the Exim ftp sites. The manual is also online at the Exim website. + + +# This file is divided into several parts, all but the first of which are +# headed by a line starting with the word "begin". Only those parts that +# are required need to be present. Blank lines, and lines starting with # +# are ignored. + + +########### IMPORTANT ########## IMPORTANT ########### IMPORTANT ########### +# # +# Whenever you change Exim's configuration file, you *must* remember to # +# HUP the Exim daemon, because it will not pick up the new configuration # +# until you do. However, any other Exim processes that are started, for # +# example, a process started by an MUA in order to send a message, will # +# see the new configuration as soon as it is in place. # +# # +# You do not need to HUP the daemon for changes in auxiliary files that # +# are referenced from this file. They are read every time they are used. # +# # +# It is usually a good idea to test a new configuration for syntactic # +# correctness before installing it (for example, by running the command # +# "exim -C /config/file.new -bV"). # +# # +########### IMPORTANT ########## IMPORTANT ########### IMPORTANT ########### + + + +###################################################################### +# MACROS # +###################################################################### +# + +# If you want to use a smarthost instead of sending directly to recipient +# domains, uncomment this macro definition and set a real hostname. +# An appropriately privileged user can then redirect email on the command-line +# in emergencies, via -D. +# +# ROUTER_SMARTHOST=MAIL.HOSTNAME.FOR.CENTRAL.SERVER.EXAMPLE + +###################################################################### +# MAIN CONFIGURATION SETTINGS # +###################################################################### +# + +# Specify your host's canonical name here. This should normally be the fully +# qualified "official" name of your host. If this option is not set, the +# uname() function is called to obtain the name. In many cases this does +# the right thing and you need not set anything explicitly. + +# primary_hostname = + + +# The next three settings create two lists of domains and one list of hosts. +# These lists are referred to later in this configuration using the syntax +# +local_domains, +relay_to_domains, and +relay_from_hosts, respectively. They +# are all colon-separated lists: + +domainlist local_domains = @ : tls-server.com +domainlist relay_to_domains = +hostlist relay_from_hosts = localhost +# (We rely upon hostname resolution working for localhost, because the default +# uncommented configuration needs to work in IPv4-only environments.) + +# Most straightforward access control requirements can be obtained by +# appropriate settings of the above options. In more complicated situations, +# you may need to modify the Access Control Lists (ACLs) which appear later in +# this file. + +# The first setting specifies your local domains, for example: +# +# domainlist local_domains = my.first.domain : my.second.domain +# +# You can use "@" to mean "the name of the local host", as in the default +# setting above. This is the name that is specified by primary_hostname, +# as specified above (or defaulted). If you do not want to do any local +# deliveries, remove the "@" from the setting above. If you want to accept mail +# addressed to your host's literal IP address, for example, mail addressed to +# "user@[192.168.23.44]", you can add "@[]" as an item in the local domains +# list. You also need to uncomment "allow_domain_literals" below. This is not +# recommended for today's Internet. + +# The second setting specifies domains for which your host is an incoming relay. +# If you are not doing any relaying, you should leave the list empty. However, +# if your host is an MX backup or gateway of some kind for some domains, you +# must set relay_to_domains to match those domains. For example: +# +# domainlist relay_to_domains = *.myco.com : my.friend.org +# +# This will allow any host to relay through your host to those domains. +# See the section of the manual entitled "Control of relaying" for more +# information. + +# The third setting specifies hosts that can use your host as an outgoing relay +# to any other host on the Internet. Such a setting commonly refers to a +# complete local network as well as the localhost. For example: +# +# hostlist relay_from_hosts = <; 127.0.0.1 ; ::1 ; 192.168.0.0/16 +# +# The "/16" is a bit mask (CIDR notation), not a number of hosts. Note that you +# have to include 127.0.0.1 if you want to allow processes on your host to send +# SMTP mail by using the loopback address. A number of MUAs use this method of +# sending mail. Often, connections are made to "localhost", which might be ::1 +# on IPv6-enabled hosts. Do not forget CIDR for your IPv6 networks. + +# All three of these lists may contain many different kinds of item, including +# wildcarded names, regular expressions, and file lookups. See the reference +# manual for details. The lists above are used in the access control lists for +# checking incoming messages. The names of these ACLs are defined here: + +acl_smtp_rcpt = acl_check_rcpt +.ifdef _HAVE_PRDR +acl_smtp_data_prdr = acl_check_prdr +.endif +acl_smtp_data = acl_check_data + +# You should not change those settings until you understand how ACLs work. + + +# If you are running a version of Exim that was compiled with the content- +# scanning extension, you can cause incoming messages to be automatically +# scanned for viruses. You have to modify the configuration in two places to +# set this up. The first of them is here, where you define the interface to +# your scanner. This example is typical for ClamAV; see the manual for details +# of what to set for other virus scanners. The second modification is in the +# acl_check_data access control list (see below). + +# av_scanner = clamd:/tmp/clamd + + +# For spam scanning, there is a similar option that defines the interface to +# SpamAssassin. You do not need to set this if you are using the default, which +# is shown in this commented example. As for virus scanning, you must also +# modify the acl_check_data access control list to enable spam scanning. + +# spamd_address = 127.0.0.1 783 + + +# If Exim is compiled with support for TLS, you may want to change the +# following option so that Exim disallows certain clients from makeing encrypted +# connections. The default is to allow all. +# In the authenticators section below, there are template configurations for +# plaintext username/password authentication. This kind of authentication is +# only safe when used within a TLS connection, so the authenticators will only +# work if TLS is allowed here. + +# This is equivalent to the default. + +tls_advertise_hosts = * + +# Specify the location of the Exim server's TLS certificate and private key. +# The private key must not be encrypted (password protected). You can put +# the certificate and private key in the same file, in which case you only +# need the first setting, or in separate files, in which case you need both +# options. + +tls_certificate = /etc/ssl/cert-data/tls-server.com-chain.crt +tls_privatekey = /etc/ssl/cert-data/tls-server.com.key + +# For OpenSSL, prefer EC- over RSA-authenticated ciphers +.ifdef _HAVE_OPENSSL +tls_require_ciphers = ECDSA:RSA:!COMPLEMENTOFDEFAULT +.endif + +# Don't offer resumption to (most) MUAs, who we don't want to reuse +# tickets. Once the TLS extension for vended ticket numbers comes +# though, re-examine since resumption on a single-use ticket is still a benefit. +.ifdef _HAVE_TLS_RESUME +tls_resumption_hosts = ${if inlist {$received_port}{587:465} {:}{*}} +.endif + +# In order to support roaming users who wish to send email from anywhere, +# you may want to make Exim listen on other ports as well as port 25, in +# case these users need to send email from a network that blocks port 25. +# The standard port for this purpose is port 587, the "message submission" +# port. See RFC 4409 for details. Microsoft MUAs cannot be configured to +# talk the message submission protocol correctly, so if you need to support +# them you should also allow TLS-on-connect on the traditional but +# non-standard port 465. + +daemon_smtp_ports = 25 : 465 : 587 +tls_on_connect_ports = 465 + + +# Specify the domain you want to be added to all unqualified addresses +# here. An unqualified address is one that does not contain an "@" character +# followed by a domain. For example, "caesar@rome.example" is a fully qualified +# address, but the string "caesar" (i.e. just a login name) is an unqualified +# email address. Unqualified addresses are accepted only from local callers by +# default. See the recipient_unqualified_hosts option if you want to permit +# unqualified addresses from remote sources. If this option is not set, the +# primary_hostname value is used for qualification. + +# qualify_domain = + + +# If you want unqualified recipient addresses to be qualified with a different +# domain to unqualified sender addresses, specify the recipient domain here. +# If this option is not set, the qualify_domain value is used. + +# qualify_recipient = + + +# The following line must be uncommented if you want Exim to recognize +# addresses of the form "user@[10.11.12.13]" that is, with a "domain literal" +# (an IP address) instead of a named domain. The RFCs still require this form, +# but it makes little sense to permit mail to be sent to specific hosts by +# their IP address in the modern Internet. This ancient format has been used +# by those seeking to abuse hosts by using them for unwanted relaying. If you +# really do want to support domain literals, uncomment the following line, and +# see also the "domain_literal" router below. + +# allow_domain_literals + + +# No deliveries will ever be run under the uids of users specified by +# never_users (a colon-separated list). An attempt to do so causes a panic +# error to be logged, and the delivery to be deferred. This is a paranoic +# safety catch. There is an even stronger safety catch in the form of the +# FIXED_NEVER_USERS setting in the configuration for building Exim. The list of +# users that it specifies is built into the binary, and cannot be changed. The +# option below just adds additional users to the list. The default for +# FIXED_NEVER_USERS is "root", but just to be absolutely sure, the default here +# is also "root". + +# Note that the default setting means you cannot deliver mail addressed to root +# as if it were a normal user. This isn't usually a problem, as most sites have +# an alias for root that redirects such mail to a human administrator. + +never_users = root + + +# The setting below causes Exim to do a reverse DNS lookup on all incoming +# IP calls, in order to get the true host name. If you feel this is too +# expensive, you can specify the networks for which a lookup is done, or +# remove the setting entirely. + +#host_lookup = + + +# The setting below causes Exim to try to initialize the system resolver +# library with DNSSEC support. It has no effect if your library lacks +# DNSSEC support. + +dns_dnssec_ok = 1 + + +# The settings below cause Exim to make RFC 1413 (ident) callbacks +# for all incoming SMTP calls. You can limit the hosts to which these +# calls are made, and/or change the timeout that is used. If you set +# the timeout to zero, all RFC 1413 calls are disabled. RFC 1413 calls +# are cheap and can provide useful information for tracing problem +# messages, but some hosts and firewalls have problems with them. +# This can result in a timeout instead of an immediate refused +# connection, leading to delays on starting up SMTP sessions. +# (The default was reduced from 30s to 5s for release 4.61. and to +# disabled for release 4.86) +# +#rfc1413_hosts = * +#rfc1413_query_timeout = 5s + + +# Enable an efficiency feature. We advertise the feature; clients +# may request to use it. For multi-recipient mails we then can +# reject or accept per-user after the message is received. +# This supports recipient-dependent content filtering; without it +# you have to temp-reject any recipients after the first that have +# incompatible filtering, and do the filtering in the data ACL. +# Even with this enabled, you must support the old style for peers +# not flagging support for PRDR (visible via $prdr_requested). +# +.ifdef _HAVE_PRDR +prdr_enable = true +.endif + + +# By default, Exim expects all envelope addresses to be fully qualified, that +# is, they must contain both a local part and a domain. If you want to accept +# unqualified addresses (just a local part) from certain hosts, you can specify +# these hosts by setting one or both of +# +# sender_unqualified_hosts = +# recipient_unqualified_hosts = +# +# to control sender and recipient addresses, respectively. When this is done, +# unqualified addresses are qualified using the settings of qualify_domain +# and/or qualify_recipient (see above). + + +# Unless you run a high-volume site you probably want more logging +# detail than the default. Adjust to suit. + +log_selector = +smtp_protocol_error +smtp_syntax_error \ + +tls_certificate_verified + + +# If you want Exim to support the "percent hack" for certain domains, +# uncomment the following line and provide a list of domains. The "percent +# hack" is the feature by which mail addressed to x%y@z (where z is one of +# the domains listed) is locally rerouted to x@y and sent on. If z is not one +# of the "percent hack" domains, x%y is treated as an ordinary local part. This +# hack is rarely needed nowadays; you should not enable it unless you are sure +# that you really need it. +# +# percent_hack_domains = +# +# As well as setting this option you will also need to remove the test +# for local parts containing % in the ACL definition below. + + +# When Exim can neither deliver a message nor return it to sender, it "freezes" +# the delivery error message (aka "bounce message"). There are also other +# circumstances in which messages get frozen. They will stay on the queue for +# ever unless one of the following options is set. + +# This option unfreezes frozen bounce messages after two days, tries +# once more to deliver them, and ignores any delivery failures. + +ignore_bounce_errors_after = 2d + +# This option cancels (removes) frozen messages that are older than a week. + +timeout_frozen_after = 7d + + +# By default, messages that are waiting on Exim's queue are all held in a +# single directory called "input" which is itself within Exim's spool +# directory. (The default spool directory is specified when Exim is built, and +# is often /var/spool/exim/.) Exim works best when its queue is kept short, but +# there are circumstances where this is not always possible. If you uncomment +# the setting below, messages on the queue are held in 62 subdirectories of +# "input" instead of all in the same directory. The subdirectories are called +# 0, 1, ... A, B, ... a, b, ... z. This has two benefits: (1) If your file +# system degrades with many files in one directory, this is less likely to +# happen; (2) Exim can process the queue one subdirectory at a time instead of +# all at once, which can give better performance with large queues. + +# split_spool_directory = true + + +# If you're in a part of the world where ASCII is not sufficient for most +# text, then you're probably familiar with RFC2047 message header extensions. +# By default, Exim adheres to the specification, including a limit of 76 +# characters to a line, with encoded words fitting within a line. +# If you wish to use decoded headers in message filters in such a way +# that successful decoding of malformed messages matters, you may wish to +# configure Exim to be more lenient. +# +# check_rfc2047_length = false +# +# In particular, the Exim maintainers have had multiple reports of problems +# from Russian administrators of issues until they disable this check, +# because of some popular, yet buggy, mail composition software. + + +# If you wish to be strictly RFC compliant, or if you know you'll be +# exchanging email with systems that are not 8-bit clean, then you may +# wish to disable advertising 8BITMIME. Uncomment this option to do so. + +# accept_8bitmime = false + + +# Exim does not make use of environment variables itself. However, +# libraries that Exim uses (e.g. LDAP) depend on specific environment settings. +# There are two lists: keep_environment for the variables we trust, and +# add_environment for variables we want to set to a specific value. +# Note that TZ is handled separately by the timezone runtime option +# and TIMEZONE_DEFAULT buildtime option. + +# keep_environment = ^LDAP +# add_environment = PATH=/usr/bin::/bin + + + +###################################################################### +# ACL CONFIGURATION # +# Specifies access control lists for incoming SMTP mail # +###################################################################### + +begin acl + +# This access control list is used for every RCPT command in an incoming +# SMTP message. The tests are run in order until the address is either +# accepted or denied. + +acl_check_rcpt: + + # Accept if the source is local SMTP (i.e. not over TCP/IP). We do this by + # testing for an empty sending host field. + + accept hosts = : + control = dkim_disable_verify + + ############################################################################# + # The following section of the ACL is concerned with local parts that contain + # @ or % or ! or / or | or dots in unusual places. + # + # The characters other than dots are rarely found in genuine local parts, but + # are often tried by people looking to circumvent relaying restrictions. + # Therefore, although they are valid in local parts, these rules lock them + # out, as a precaution. + # + # Empty components (two dots in a row) are not valid in RFC 2822, but Exim + # allows them because they have been encountered. (Consider local parts + # constructed as "firstinitial.secondinitial.familyname" when applied to + # someone like me, who has no second initial.) However, a local part starting + # with a dot or containing /../ can cause trouble if it is used as part of a + # file name (e.g. for a mailing list). This is also true for local parts that + # contain slashes. A pipe symbol can also be troublesome if the local part is + # incorporated unthinkingly into a shell command line. + # + # Two different rules are used. The first one is stricter, and is applied to + # messages that are addressed to one of the local domains handled by this + # host. The line "domains = +local_domains" restricts it to domains that are + # defined by the "domainlist local_domains" setting above. The rule blocks + # local parts that begin with a dot or contain @ % ! / or |. If you have + # local accounts that include these characters, you will have to modify this + # rule. + + deny message = Restricted characters in address + domains = +local_domains + local_parts = ^[.] : ^.*[@%!/|] + + # The second rule applies to all other domains, and is less strict. The line + # "domains = !+local_domains" restricts it to domains that are NOT defined by + # the "domainlist local_domains" setting above. The exclamation mark is a + # negating operator. This rule allows your own users to send outgoing + # messages to sites that use slashes and vertical bars in their local parts. + # It blocks local parts that begin with a dot, slash, or vertical bar, but + # allows these characters within the local part. However, the sequence /../ + # is barred. The use of @ % and ! is blocked, as before. The motivation here + # is to prevent your users (or your users' viruses) from mounting certain + # kinds of attack on remote sites. + + deny message = Restricted characters in address + domains = !+local_domains + local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./ + ############################################################################# + + # Accept mail to postmaster in any local domain, regardless of the source, + # and without verifying the sender. + + accept local_parts = postmaster + domains = +local_domains + + # Deny unless the sender address can be verified. + + require verify = sender + + # Reject all RCPT commands after too many bad recipients + # This is partly a defense against spam abuse and partly attacker abuse. + # Real senders should manage, by the time they get to 10 RCPT directives, + # to have had at least half of them be real addresses. + # + # This is a lightweight check and can protect you against repeated + # invocations of more heavy-weight checks which would come after it. + + deny condition = ${if and {\ + {>{$rcpt_count}{10}}\ + {<{$recipients_count}{${eval:$rcpt_count/2}}} }} + message = Rejected for too many bad recipients + logwrite = REJECT [$sender_host_address]: bad recipient count high [${eval:$rcpt_count-$recipients_count}] + + # Accept if the message comes from one of the hosts for which we are an + # outgoing relay. It is assumed that such hosts are most likely to be MUAs, + # so we set control=submission to make Exim treat the message as a + # submission. It will fix up various errors in the message, for example, the + # lack of a Date: header line. If you are actually relaying out out from + # MTAs, you may want to disable this. If you are handling both relaying from + # MTAs and submissions from MUAs you should probably split them into two + # lists, and handle them differently. + + # Recipient verification is omitted here, because in many cases the clients + # are dumb MUAs that don't cope well with SMTP error responses. If you are + # actually relaying out from MTAs, you should probably add recipient + # verification here. + + # Note that, by putting this test before any DNS black list checks, you will + # always accept from these hosts, even if they end up on a black list. The + # assumption is that they are your friends, and if they get onto a black + # list, it is a mistake. + + accept hosts = +relay_from_hosts + control = submission + control = dkim_disable_verify + + # Accept if the message arrived over an authenticated connection, from + # any host. Again, these messages are usually from MUAs, so recipient + # verification is omitted, and submission mode is set. And again, we do this + # check before any black list tests. + + accept authenticated = * + control = submission + control = dkim_disable_verify + + # Insist that any other recipient address that we accept is either in one of + # our local domains, or is in a domain for which we explicitly allow + # relaying. Any other domain is rejected as being unacceptable for relaying. + + require message = relay not permitted + domains = +local_domains : +relay_to_domains + + # We also require all accepted addresses to be verifiable. This check will + # do local part verification for local domains, but only check the domain + # for remote domains. The only way to check local parts for the remote + # relay domains is to use a callout (add /callout), but please read the + # documentation about callouts before doing this. + + require verify = recipient + + ############################################################################# + # There are no default checks on DNS black lists because the domains that + # contain these lists are changing all the time. However, here are two + # examples of how you can get Exim to perform a DNS black list lookup at this + # point. The first one denies, whereas the second just warns. + # + # deny dnslists = black.list.example + # message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text + # + # warn dnslists = black.list.example + # add_header = X-Warning: $sender_host_address is in a black list at $dnslist_domain + # log_message = found in $dnslist_domain + ############################################################################# + + ############################################################################# + # This check is commented out because it is recognized that not every + # sysadmin will want to do it. If you enable it, the check performs + # Client SMTP Authorization (csa) checks on the sending host. These checks + # do DNS lookups for SRV records. The CSA proposal is currently (May 2005) + # an Internet draft. You can, of course, add additional conditions to this + # ACL statement to restrict the CSA checks to certain hosts only. + # + # require verify = csa + ############################################################################# + + ############################################################################# + # If doing per-user content filtering then recipients with filters different + # to the first recipient must be deferred unless the sender talks PRDR. + # + # defer !condition = $prdr_requested + # condition = ${if > {0}{$recipients_count}} + # condition = ${if !eq {$acl_m_content_filter} \ + # {${lookup PER_RCPT_CONTENT_FILTER}}} + # warn !condition = $prdr_requested + # condition = ${if > {0}{$recipients_count}} + # set acl_m_content_filter = ${lookup PER_RCPT_CONTENT_FILTER} + ############################################################################# + + # At this point, the address has passed all the checks that have been + # configured, so we accept it unconditionally. + + accept + + +# This ACL is used once per recipient, for multi-recipient messages, if +# we advertised PRDR. It can be used to perform receipient-dependent +# header- and body- based filtering and rejections. +# We set a variable to record that PRDR was active used, so that checking +# in the data ACL can be skipped. + +.ifdef _HAVE_PRDR +acl_check_prdr: + warn set acl_m_did_prdr = y + + ############################################################################# + # do lookup on filtering, with $local_part@$domain, deny on filter match + # + # deny set acl_m_content_filter = ${lookup PER_RCPT_CONTENT_FILTER} + # condition = ... + ############################################################################# + + accept +.endif + +# This ACL is used after the contents of a message have been received. This +# is the ACL in which you can test a message's headers or body, and in +# particular, this is where you can invoke external virus or spam scanners. +# Some suggested ways of configuring these tests are shown below, commented +# out. Without any tests, this ACL accepts all messages. If you want to use +# such tests, you must ensure that Exim is compiled with the content-scanning +# extension (WITH_CONTENT_SCAN=yes in Local/Makefile). + +acl_check_data: + + # Deny if the message contains an overlong line. Per the standards + # we should never receive one such via SMTP. + # + deny condition = ${if > {$max_received_linelength}{998}} + message = maximum allowed line length is 998 octets, \ + got $max_received_linelength + + # Deny if the headers contain badly-formed addresses. + # + deny !verify = header_syntax + message = header syntax + log_message = header syntax ($acl_verify_message) + + # Deny if the message contains a virus. Before enabling this check, you + # must install a virus scanner and set the av_scanner option above. + # + # deny malware = * + # message = This message contains a virus ($malware_name). + + # Add headers to a message if it is judged to be spam. Before enabling this, + # you must install SpamAssassin. You may also need to set the spamd_address + # option above. + # + # warn spam = nobody + # add_header = X-Spam_score: $spam_score\n\ + # X-Spam_score_int: $spam_score_int\n\ + # X-Spam_bar: $spam_bar\n\ + # X-Spam_report: $spam_report + + ############################################################################# + # No more tests if PRDR was actively used. + # accept condition = ${if def:acl_m_did_prdr} + # + # To get here, all message recipients must have identical per-user + # content filtering (enforced by RCPT ACL). Do lookup for filter + # and deny on match. + # + # deny set acl_m_content_filter = ${lookup PER_RCPT_CONTENT_FILTER} + # condition = ... + ############################################################################# + + + # Accept the message. + + accept + + + +###################################################################### +# ROUTERS CONFIGURATION # +# Specifies how addresses are handled # +###################################################################### +# THE ORDER IN WHICH THE ROUTERS ARE DEFINED IS IMPORTANT! # +# An address is passed to each router in turn until it is accepted. # +###################################################################### + +begin routers + +# This router routes to remote hosts over SMTP by explicit IP address, +# when an email address is given in "domain literal" form, for example, +# . The RFCs require this facility. However, it is +# little-known these days, and has been exploited by evil people seeking +# to abuse SMTP relays. Consequently it is commented out in the default +# configuration. If you uncomment this router, you also need to uncomment +# allow_domain_literals above, so that Exim can recognize the syntax of +# domain literal addresses. + +# domain_literal: +# driver = ipliteral +# domains = ! +local_domains +# transport = remote_smtp + + +# This router can be used when you want to send all mail to a +# server which handles DNS lookups for you; an ISP will typically run such +# a server for their customers. The hostname in route_data comes from the +# macro defined at the top of the file. If not defined, then we'll use the +# dnslookup router below instead. +# Beware that the hostname is specified again in the Transport. + +.ifdef ROUTER_SMARTHOST + +smarthost: + driver = manualroute + domains = ! +local_domains + transport = smarthost_smtp + route_data = ROUTER_SMARTHOST + ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1 + no_more + +.else + +# This router routes addresses that are not in local domains by doing a DNS +# lookup on the domain name. The exclamation mark that appears in "domains = ! +# +local_domains" is a negating operator, that is, it can be read as "not". The +# recipient's domain must not be one of those defined by "domainlist +# local_domains" above for this router to be used. +# +# If the router is used, any domain that resolves to 0.0.0.0 or to a loopback +# interface address (127.0.0.0/8) is treated as if it had no DNS entry. Note +# that 0.0.0.0 is the same as 0.0.0.0/32, which is commonly treated as the +# local host inside the network stack. It is not 0.0.0.0/0, the default route. +# If the DNS lookup fails, no further routers are tried because of the no_more +# setting, and consequently the address is unrouteable. + +dnslookup: + driver = dnslookup + domains = ! +local_domains + transport = remote_smtp + ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 +# if ipv6-enabled then instead use: +# ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1 + no_more + +# This closes the ROUTER_SMARTHOST ifdef around the choice of routing for +# off-site mail. +.endif + + +# The remaining routers handle addresses in the local domain(s), that is those +# domains that are defined by "domainlist local_domains" above. + + +# This router handles aliasing using a linearly searched alias file with the +# name /etc/mail/aliases. When this configuration is installed automatically, +# the name gets inserted into this file from whatever is set in Exim's +# build-time configuration. The default path is the traditional /etc/mail/aliases. +# If you install this configuration by hand, you need to specify the correct +# path in the "data" setting below. +# +##### NB You must ensure that the alias file exists. It used to be the case +##### NB that every Unix had that file, because it was the Sendmail default. +##### NB These days, there are systems that don't have it. Your aliases +##### NB file should at least contain an alias for "postmaster". +# +# If any of your aliases expand to pipes or files, you will need to set +# up a user and a group for these deliveries to run under. You can do +# this by uncommenting the "user" option below (changing the user name +# as appropriate) and adding a "group" option if necessary. Alternatively, you +# can specify "user" on the transports that are used. Note that the transports +# listed below are the same as are used for .forward files; you might want +# to set up different ones for pipe and file deliveries from aliases. + +system_aliases: + driver = redirect + allow_fail + allow_defer + data = ${lookup{$local_part}lsearch{/etc/mail/aliases}} +# user = exim + file_transport = address_file + pipe_transport = address_pipe + + +# This router handles forwarding using traditional .forward files in users' +# home directories. If you want it also to allow mail filtering when a forward +# file starts with the string "# Exim filter" or "# Sieve filter", uncomment +# the "allow_filter" option. + +# The no_verify setting means that this router is skipped when Exim is +# verifying addresses. Similarly, no_expn means that this router is skipped if +# Exim is processing an EXPN command. + +# If you want this router to treat local parts with suffixes introduced by "-" +# or "+" characters as if the suffixes did not exist, uncomment the two local_ +# part_suffix options. Then, for example, xxxx-foo@your.domain will be treated +# in the same way as xxxx@your.domain by this router. Because this router is +# not used for verification, if you choose to uncomment those options, then you +# will *need* to make the same change to the localuser router. (There are +# other approaches, if this is undesirable, but they add complexity). + +# The check_ancestor option means that if the forward file generates an +# address that is an ancestor of the current one, the current one gets +# passed on instead. This covers the case where A is aliased to B and B +# has a .forward file pointing to A. + +# The three transports specified at the end are those that are used when +# forwarding generates a direct delivery to a file, or to a pipe, or sets +# up an auto-reply, respectively. + +userforward: + driver = redirect + check_local_user +# local_part_suffix = +* : -* +# local_part_suffix_optional + file = $home/.forward +# allow_filter + no_verify + no_expn + check_ancestor + file_transport = address_file + pipe_transport = address_pipe + reply_transport = address_reply + + +# This router matches local user mailboxes. If the router fails, the error +# message is "Unknown user". + +# If you want this router to treat local parts with suffixes introduced by "-" +# or "+" characters as if the suffixes did not exist, uncomment the two local_ +# part_suffix options. Then, for example, xxxx-foo@your.domain will be treated +# in the same way as xxxx@your.domain by this router. + +localuser: + driver = accept + check_local_user +# local_part_suffix = +* : -* +# local_part_suffix_optional + transport = local_delivery + cannot_route_message = Unknown user + + + +###################################################################### +# TRANSPORTS CONFIGURATION # +###################################################################### +# ORDER DOES NOT MATTER # +# Only one appropriate transport is called for each delivery. # +###################################################################### + +# A transport is used only when referenced from a router that successfully +# handles an address. + +begin transports + + +# This transport is used for delivering messages over SMTP connections. + +remote_smtp: + driver = smtp +.ifdef _HAVE_TLS_RESUME + tls_resumption_hosts = * +.endif + + +# This transport is used for delivering messages to a smarthost, if the +# smarthost router is enabled. This starts from the same basis as +# "remote_smtp" but then turns on various security options, because +# we assume that if you're told "use smarthost.example.org as the smarthost" +# then there will be TLS available, with a verifiable certificate for that +# hostname, using decent TLS. + +smarthost_smtp: + driver = smtp + multi_domain + # +.ifdef _HAVE_TLS + # Comment out any of these which you have to, then file a Support + # request with your smarthost provider to get things fixed: + hosts_require_tls = * + tls_verify_hosts = * + # As long as tls_verify_hosts is enabled, this this will have no effect, + # but if you have to comment it out then this will at least log whether + # you succeed or not: + tls_try_verify_hosts = * + # + # The SNI name should match the name which we'll expect to verify; + # many mail systems don't use SNI and this doesn't matter, but if it does, + # we need to send a name which the remote site will recognize. + # This _should_ be the name which the smarthost operators specified as + # the hostname for sending your mail to. + tls_sni = tls-server.com + + tls_alpn = "smtp" + hosts_require_alpn = * +.ifdef _HAVE_OPENSSL + tls_require_ciphers = HIGH:!MD5:!SHA1:-TLSv1.0:-SSLv3:-TLSv1.1 + #openssl_options = "+no_tlsv1_1" +.endif +.ifdef _HAVE_GNUTLS + tls_require_ciphers = SECURE192:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1-TLS1.2 +.endif +.ifdef _HAVE_TLS_RESUME + tls_resumption_hosts = * +.endif +.endif + + + + +# This transport is used for local delivery to user mailboxes in traditional +# BSD mailbox format. By default it will be run under the uid and gid of the +# local user, and requires the sticky bit to be set on the /var/mail directory. +# Some systems use the alternative approach of running mail deliveries under a +# particular group instead of using the sticky bit. The commented options below +# show how this can be done. + +local_delivery: + driver = appendfile + file = /var/mail/$local_part_data + delivery_date_add + envelope_to_add + return_path_add +# group = mail +# mode = 0660 + + +# This transport is used for handling pipe deliveries generated by alias or +# .forward files. If the pipe generates any standard output, it is returned +# to the sender of the message as a delivery error. Set return_fail_output +# instead of return_output if you want this to happen only when the pipe fails +# to complete normally. You can set different transports for aliases and +# forwards if you want to - see the references to address_pipe in the routers +# section above. + +address_pipe: + driver = pipe + return_output + + +# This transport is used for handling deliveries directly to files that are +# generated by aliasing or forwarding. + +address_file: + driver = appendfile + delivery_date_add + envelope_to_add + return_path_add + + +# This transport is used for handling autoreplies generated by the filtering +# option of the userforward router. + +address_reply: + driver = autoreply + + + +###################################################################### +# RETRY CONFIGURATION # +###################################################################### + +begin retry + +# This single retry rule applies to all domains and all errors. It specifies +# retries every 15 minutes for 2 hours, then increasing retry intervals, +# starting at 1 hour and increasing each time by a factor of 1.5, up to 16 +# hours, then retries every 6 hours until 4 days have passed since the first +# failed delivery. + +# WARNING: If you do not have any retry rules at all (this section of the +# configuration is non-existent or empty), Exim will not do any retries of +# messages that fail to get delivered at the first attempt. The effect will +# be to treat temporary errors as permanent. Therefore, DO NOT remove this +# retry rule unless you really don't want any retries. + +# Address or Domain Error Retries +# ----------------- ----- ------- + +* * F,2h,15m; G,16h,1h,1.5; F,4d,6h + + + +###################################################################### +# REWRITE CONFIGURATION # +###################################################################### + +# There are no rewriting specifications in this default configuration file. + +begin rewrite + + + +###################################################################### +# AUTHENTICATION CONFIGURATION # +###################################################################### + +# The following authenticators support plaintext username/password +# authentication using the standard PLAIN mechanism and the traditional +# but non-standard LOGIN mechanism, with Exim acting as the server. +# PLAIN and LOGIN are enough to support most MUA software. +# +# These authenticators are not complete: you need to change the +# server_condition settings to specify how passwords are verified. +# They are set up to offer authentication to the client only if the +# connection is encrypted with TLS, so you also need to add support +# for TLS. See the global configuration options section at the start +# of this file for more about TLS. +# +# The default RCPT ACL checks for successful authentication, and will accept +# messages from authenticated users from anywhere on the Internet. + +# special sections always start with a "begin" +begin authenticators + + PLAIN: + # authentication protocol is plain text + driver = plaintext + # authentication is offered as plain text + public_name = PLAIN + server_prompts = : + # authentication is successful, if the password provided ($auth3) + # equals the password found in a lookup file for user ($auth2) + server_condition = ${if eq{$auth3}{${lookup{$auth2}dbm{/etc/authpwd}}}} + server_set_id = $auth2 + # only offer plain text authentication after TLS is been negotiated + server_advertise_condition = ${if def:tls_in_cipher} + +# PLAIN authentication has no server prompts. The client sends its +# credentials in one lump, containing an authorization ID (which we do not +# use), an authentication ID, and a password. The latter two appear as +# $auth2 and $auth3 in the configuration and should be checked against a +# valid username and password. In a real configuration you would typically +# use $auth2 as a lookup key, and compare $auth3 against the result of the +# lookup, perhaps using the crypteq{}{} condition. + +#PLAIN: +# driver = plaintext +# server_set_id = $auth2 +# server_prompts = : +# server_condition = Authentication is not yet configured +# server_advertise_condition = ${if def:tls_in_cipher } + +# LOGIN authentication has traditional prompts and responses. There is no +# authorization ID in this mechanism, so unlike PLAIN the username and +# password are $auth1 and $auth2. Apart from that you can use the same +# server_condition setting for both authenticators. + +#LOGIN: +# driver = plaintext +# server_set_id = $auth1 +# server_prompts = <| Username: | Password: +# server_condition = Authentication is not yet configured +# server_advertise_condition = ${if def:tls_in_cipher } + + +###################################################################### +# CONFIGURATION FOR local_scan() # +###################################################################### + +# If you have built Exim to include a local_scan() function that contains +# tables for private options, you can define those options here. Remember to +# uncomment the "begin" line. It is commented by default because it provokes +# an error with Exim binaries that are not built with LOCAL_SCAN_HAS_OPTIONS +# set in the Local/Makefile. + +# begin local_scan + + +# End of Exim configuration file \ No newline at end of file diff --git a/evaluation-servers/exim/run.sh b/evaluation-servers/exim/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/exim/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/filezilla-server/Dockerfile b/evaluation-servers/filezilla-server/Dockerfile new file mode 100644 index 0000000..0595f87 --- /dev/null +++ b/evaluation-servers/filezilla-server/Dockerfile @@ -0,0 +1,27 @@ +FROM tls-baseimage + + +# RUN cat /etc/ssl/cert-data/tls-server.com.key >> /etc/ssl/private/pure-ftpd.pem +# RUN cat /etc/ssl/cert-data/tls-server.com-chain.crt >> /etc/ssl/private/pure-ftpd.pem +RUN apk add gnutls-dev wxgtk-dev pugixml-dev libexecinfo-dev + +ARG LIBVERSION=0.34.2 +WORKDIR /build +RUN wget https://download.filezilla-project.org/libfilezilla/libfilezilla-${LIBVERSION}.tar.bz2 +RUN tar -xvf libfilezilla-${LIBVERSION}.tar.bz2 +WORKDIR /build/libfilezilla-${LIBVERSION} +RUN ./configure --prefix=/usr +RUN make +RUN make install +#RUN mv lib/.libs/libfilezilla.a /usr/lib/ + +ARG VERSION=1.1.0 +WORKDIR /build +RUN wget https://download.filezilla-project.org/server/FileZilla_Server_${VERSION}_src.tar.bz2 +RUN tar -xvf FileZilla_Server_${VERSION}_src.tar.bz2 +WORKDIR /build/filezilla-server-${VERSION} +RUN ./configure --prefix=/usr +RUN make +RUN mv src/server/filezilla-server /bin/ + +CMD ["filezilla-server"] \ No newline at end of file diff --git a/evaluation-servers/filezilla-server/build.sh b/evaluation-servers/filezilla-server/build.sh new file mode 100755 index 0000000..94cd35e --- /dev/null +++ b/evaluation-servers/filezilla-server/build.sh @@ -0,0 +1 @@ +docker build . -t tls-filezillaserver \ No newline at end of file diff --git a/evaluation-servers/filezilla-server/docker-compose.yml b/evaluation-servers/filezilla-server/docker-compose.yml new file mode 100644 index 0000000..3f009af --- /dev/null +++ b/evaluation-servers/filezilla-server/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + filezillaserver: + image: tls-filezillaserver + scanner: + image: tlsscanner + depends_on: + - filezillaserver + command: [ "-connect", "filezillaserver:21", "-server_name", "tls-server.com", "-starttls", "ftp", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/filezilla-server/run.sh b/evaluation-servers/filezilla-server/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/filezilla-server/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/lighttpd/Dockerfile b/evaluation-servers/lighttpd/Dockerfile new file mode 100644 index 0000000..772f915 --- /dev/null +++ b/evaluation-servers/lighttpd/Dockerfile @@ -0,0 +1,20 @@ +FROM tls-openssl + + +# RUN cat /etc/ssl/cert-data/tls-server.com.key >> /etc/ssl/private/pure-ftpd.pem +# RUN cat /etc/ssl/cert-data/tls-server.com-chain.crt >> /etc/ssl/private/pure-ftpd.pem +RUN apk add pcre-dev zlib-dev bsd-compat-headers + +ARG VERSION=1.4.63 +WORKDIR /build +RUN wget https://download.lighttpd.net/lighttpd/releases-1.4.x/lighttpd-${VERSION}.tar.gz +RUN tar -xvf lighttpd-${VERSION}.tar.gz +WORKDIR /build/lighttpd-${VERSION} +RUN ./autogen.sh +RUN ./configure -C --with-openssl +RUN make check +RUN make install + +ADD lighttpd.conf /etc/lighttpd.conf + +CMD ["lighttpd", "-D", "-f", "/etc/lighttpd.conf"] \ No newline at end of file diff --git a/evaluation-servers/lighttpd/Dockerfile-mbedtls b/evaluation-servers/lighttpd/Dockerfile-mbedtls new file mode 100644 index 0000000..c6715d1 --- /dev/null +++ b/evaluation-servers/lighttpd/Dockerfile-mbedtls @@ -0,0 +1,20 @@ +FROM tls-baseimage + + +# RUN cat /etc/ssl/cert-data/tls-server.com.key >> /etc/ssl/private/pure-ftpd.pem +# RUN cat /etc/ssl/cert-data/tls-server.com-chain.crt >> /etc/ssl/private/pure-ftpd.pem +RUN apk add mbedtls-dev pcre-dev zlib-dev bsd-compat-headers + +ARG VERSION=1.4.63 +WORKDIR /build +RUN wget https://download.lighttpd.net/lighttpd/releases-1.4.x/lighttpd-${VERSION}.tar.gz +RUN tar -xvf lighttpd-${VERSION}.tar.gz +WORKDIR /build/lighttpd-${VERSION} +RUN ./autogen.sh +RUN ./configure -C --with-mbedtls +RUN make check +RUN make install + +ADD lighttpd-mbedtls.conf /etc/lighttpd.conf + +CMD ["lighttpd", "-D", "-f", "/etc/lighttpd.conf"] \ No newline at end of file diff --git a/evaluation-servers/lighttpd/build.sh b/evaluation-servers/lighttpd/build.sh new file mode 100755 index 0000000..6bf2cf4 --- /dev/null +++ b/evaluation-servers/lighttpd/build.sh @@ -0,0 +1,2 @@ +docker build . -t tls-lighttpd-openssl +docker build . -t tls-lighttpd-mbedtls -f Dockerfile-mbedtls \ No newline at end of file diff --git a/evaluation-servers/lighttpd/docker-compose.yml b/evaluation-servers/lighttpd/docker-compose.yml new file mode 100644 index 0000000..fae2731 --- /dev/null +++ b/evaluation-servers/lighttpd/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + lighttpd-openssl: + image: tls-lighttpd-openssl + lighttpd-mbedtls: + image: tls-lighttpd-mbedtls + scanner: + image: tlsscanner + depends_on: + - lighttpd-openssl + command: [ "-connect", "lighttpd-openssl:443", "-server_name", "tls-server.com", "-scanDetail", "QUICK" ] + scanner2: + image: tlsscanner + depends_on: + - lighttpd-mbedtls + command: [ "-connect", "lighttpd-mbedtls:443", "-server_name", "tls-server.com", "-scanDetail", "QUICK" ] \ No newline at end of file diff --git a/evaluation-servers/lighttpd/lighttpd-mbedtls.conf b/evaluation-servers/lighttpd/lighttpd-mbedtls.conf new file mode 100644 index 0000000..1a7fff4 --- /dev/null +++ b/evaluation-servers/lighttpd/lighttpd-mbedtls.conf @@ -0,0 +1,53 @@ +############################################################################### +# Default lighttpd.conf for Gentoo. +# $Header: /var/cvsroot/gentoo-x86/www-servers/lighttpd/files/conf/lighttpd.conf,v 1.3 2005/09/01 14:22:35 ka0ttic Exp $ +############################################################################### + +# {{{ variables +var.basedir = "/var/www/localhost" +#var.logdir = "/var/log/lighttpd" +var.statedir = "/var/lib/lighttpd" +# }}} + +# {{{ modules +# At the very least, mod_access and mod_accesslog should be enabled. +# All other modules should only be loaded if necessary. +# NOTE: the order of modules is important. +server.modules = ( + + "mod_access", + "mod_mbedtls", + + "mod_accesslog" +) + +server.document-root = var.basedir + "/htdocs" +server.pid-file = "/run/lighttpd.pid" + + +server.indexfiles = ("index.php", "index.html", + "index.htm", "default.htm") + +server.follow-symlink = "enable" + +static-file.exclude-extensions = (".php", ".pl", ".cgi", ".fcgi") + +url.access-deny = ("~", ".inc") + +$SERVER["socket"] == ":443" { + ssl.engine = "enable" + ssl.pemfile = "/etc/ssl/cert-data/tls-server.com-chain.crt" + ssl.privkey = "/etc/ssl/cert-data/tls-server.com.key" + ssl.cipher-list = "EECDH+AESGCM:AES256+EECDH:CHACHA20:!SHA1:!SHA256:!SHA384" + $HTTP["host"] == "tls-server.com" { + ssl.pemfile = "/etc/ssl/cert-data/tls-server.com-chain.crt" + ssl.privkey = "/etc/ssl/cert-data/tls-server.com.key" + } +} + + + + +#ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2") +#ssl.openssl.ssl-conf-cmd += ("Options" => "-ServerPreference") +#ssl.openssl.ssl-conf-cmd += ("CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384") \ No newline at end of file diff --git a/evaluation-servers/lighttpd/lighttpd.conf b/evaluation-servers/lighttpd/lighttpd.conf new file mode 100644 index 0000000..b4a5d32 --- /dev/null +++ b/evaluation-servers/lighttpd/lighttpd.conf @@ -0,0 +1,51 @@ +############################################################################### +# Default lighttpd.conf for Gentoo. +# $Header: /var/cvsroot/gentoo-x86/www-servers/lighttpd/files/conf/lighttpd.conf,v 1.3 2005/09/01 14:22:35 ka0ttic Exp $ +############################################################################### + +# {{{ variables +var.basedir = "/var/www/localhost" +#var.logdir = "/var/log/lighttpd" +var.statedir = "/var/lib/lighttpd" +# }}} + +# {{{ modules +# At the very least, mod_access and mod_accesslog should be enabled. +# All other modules should only be loaded if necessary. +# NOTE: the order of modules is important. +server.modules = ( + + "mod_access", + "mod_openssl", + + "mod_accesslog" +) + +server.document-root = var.basedir + "/htdocs" +server.pid-file = "/run/lighttpd.pid" + + +server.indexfiles = ("index.php", "index.html", + "index.htm", "default.htm") + +server.follow-symlink = "enable" + +static-file.exclude-extensions = (".php", ".pl", ".cgi", ".fcgi") + +url.access-deny = ("~", ".inc") + +$SERVER["socket"] == ":443" { + ssl.engine = "enable" + ssl.pemfile = "/etc/ssl/cert-data/tls-server.com-chain.crt" + ssl.privkey = "/etc/ssl/cert-data/tls-server.com.key" + ssl.cipher-list = "EECDH+AESGCM:AES256+EECDH:CHACHA20:!SHA1:!SHA256:!SHA384" + $HTTP["host"] == "tls-server.com" { + ssl.pemfile = "/etc/ssl/cert-data/tls-server.com-chain.crt" + ssl.privkey = "/etc/ssl/cert-data/tls-server.com.key" + } +} + + +#ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2") +#ssl.openssl.ssl-conf-cmd += ("Options" => "-ServerPreference") +#ssl.openssl.ssl-conf-cmd += ("CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384") \ No newline at end of file diff --git a/evaluation-servers/lighttpd/run.sh b/evaluation-servers/lighttpd/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/lighttpd/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/nginx/Dockerfile b/evaluation-servers/nginx/Dockerfile new file mode 100644 index 0000000..a15b2d1 --- /dev/null +++ b/evaluation-servers/nginx/Dockerfile @@ -0,0 +1,16 @@ +FROM tls-baseimage as tls-nginx +ARG VERSION=1.21.4 +RUN apk add pcre-dev openssl-dev +WORKDIR /build +RUN wget http://nginx.org/download/nginx-${VERSION}.tar.gz +RUN tar -xvf nginx-${VERSION}.tar.gz +WORKDIR /build/nginx-${VERSION} +#ADD patch.diff /build/nginx-${VERSION}/patch.diff +#RUN patch src/http/modules/ngx_http_ssl_module.c +RUN mkdir /usr/local/nginx +RUN mkdir /usr/local/nginx/logs +RUN ./configure --conf-path=/etc/nginx.conf --with-http_ssl_module --without-http_gzip_module --with-http_v2_module +RUN make +ADD nginx.conf /etc/nginx.conf +RUN mv objs/nginx /usr/bin/nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/evaluation-servers/nginx/build.sh b/evaluation-servers/nginx/build.sh new file mode 100755 index 0000000..b215b85 --- /dev/null +++ b/evaluation-servers/nginx/build.sh @@ -0,0 +1 @@ +docker build . -t tls-nginx diff --git a/evaluation-servers/nginx/docker-compose.yml b/evaluation-servers/nginx/docker-compose.yml new file mode 100644 index 0000000..bf3a141 --- /dev/null +++ b/evaluation-servers/nginx/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + nginx: + image: tls-nginx + scanner: + image: tlsscanner + depends_on: + - nginx + command: [ "-connect", "nginx:4433", "-server_name", "tls-server.com", "-scanDetail", "QUICK"] diff --git a/evaluation-servers/nginx/nginx.conf b/evaluation-servers/nginx/nginx.conf new file mode 100644 index 0000000..3d6dc3c --- /dev/null +++ b/evaluation-servers/nginx/nginx.conf @@ -0,0 +1,20 @@ +worker_processes auto; + +events { + worker_connections 1024; +} + +http { + server { + listen 4433 ssl http2; + server_name tls-server.com; + + ssl_certificate /etc/ssl/cert-data/tls-server.com-chain.crt; + ssl_certificate_key /etc/ssl/cert-data/tls-server.com.key; + ssl_protocols TLSv1.2 TLSv1.3; + + location / { + return 404; + } + } +} \ No newline at end of file diff --git a/evaluation-servers/nginx/run.sh b/evaluation-servers/nginx/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/nginx/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/opensmtpd/Dockerfile b/evaluation-servers/opensmtpd/Dockerfile new file mode 100644 index 0000000..7ee7485 --- /dev/null +++ b/evaluation-servers/opensmtpd/Dockerfile @@ -0,0 +1,6 @@ +FROM tls-baseimagedebian +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -yq opensmtpd +ADD smtpd.conf /etc/ +RUN chmod 600 /etc/ssl/cert-data/* +CMD ["smtpd", "-d"] \ No newline at end of file diff --git a/evaluation-servers/opensmtpd/build.sh b/evaluation-servers/opensmtpd/build.sh new file mode 100755 index 0000000..c475a43 --- /dev/null +++ b/evaluation-servers/opensmtpd/build.sh @@ -0,0 +1 @@ +docker build . -t tls-opensmtpd diff --git a/evaluation-servers/opensmtpd/docker-compose.yml b/evaluation-servers/opensmtpd/docker-compose.yml new file mode 100644 index 0000000..7b0df39 --- /dev/null +++ b/evaluation-servers/opensmtpd/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + opensmtpd: + image: tls-opensmtpd + scanner: + image: tlsscanner + depends_on: + - opensmtpd + command: [ "-connect", "opensmtpd:465", "-server_name", "tls-server.com", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/opensmtpd/run.sh b/evaluation-servers/opensmtpd/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/opensmtpd/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/opensmtpd/smtpd.conf b/evaluation-servers/opensmtpd/smtpd.conf new file mode 100644 index 0000000..2a9e45e --- /dev/null +++ b/evaluation-servers/opensmtpd/smtpd.conf @@ -0,0 +1,26 @@ +# $OpenBSD: smtpd.conf,v 1.10 2018/05/24 11:40:17 gilles Exp $ + +# This is the smtpd server system-wide configuration file. +# See smtpd.conf(5) for more information. + +table aliases file:/etc/aliases + +pki tls-server.com cert "/etc/ssl/cert-data/tls-server.com-chain.crt" +pki tls-server.com key "/etc/ssl/cert-data/tls-server.com.key" + +# To accept external mail, replace with: listen on all +# +#listen on 0.0.0.0 smtps + +listen on 0.0.0.0 tls pki tls-server.com +listen on 0.0.0.0 port 465 smtps pki tls-server.com +listen on 0.0.0.0 port 587 tls-require pki tls-server.com + +action "local" maildir alias +action "relay" relay + +# Uncomment the following to accept external mail for domain "example.org" +# +# match from any for domain "example.org" action "local" +match for local action "local" +match from local for any action "relay" \ No newline at end of file diff --git a/evaluation-servers/postfix/Dockerfile b/evaluation-servers/postfix/Dockerfile new file mode 100644 index 0000000..541e564 --- /dev/null +++ b/evaluation-servers/postfix/Dockerfile @@ -0,0 +1,6 @@ +FROM tls-baseimage as tls-apache +RUN apk add postfix +COPY main.cf /etc/postfix/ +COPY master.cf /etc/postfix/ + +CMD ["/usr/sbin/postfix", "start-fg"] \ No newline at end of file diff --git a/evaluation-servers/postfix/build.sh b/evaluation-servers/postfix/build.sh new file mode 100755 index 0000000..3ab1ecb --- /dev/null +++ b/evaluation-servers/postfix/build.sh @@ -0,0 +1 @@ +docker build . -t tls-postfix diff --git a/evaluation-servers/postfix/docker-compose.yml b/evaluation-servers/postfix/docker-compose.yml new file mode 100644 index 0000000..a572abd --- /dev/null +++ b/evaluation-servers/postfix/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + postfix: + image: tls-postfix + scanner: + image: tlsscanner + depends_on: + - postfix + command: [ "-connect", "postfix:465", "-server_name", "tls-server.com", "-starttls", "SMTP", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/postfix/main.cf b/evaluation-servers/postfix/main.cf new file mode 100644 index 0000000..990b6a2 --- /dev/null +++ b/evaluation-servers/postfix/main.cf @@ -0,0 +1,686 @@ +# Global Postfix configuration file. This file lists only a subset +# of all parameters. For the syntax, and for a complete parameter +# list, see the postconf(5) manual page (command: "man 5 postconf"). +# +# For common configuration examples, see BASIC_CONFIGURATION_README +# and STANDARD_CONFIGURATION_README. To find these documents, use +# the command "postconf html_directory readme_directory", or go to +# http://www.postfix.org/BASIC_CONFIGURATION_README.html etc. +# +# For best results, change no more than 2-3 parameters at a time, +# and test if Postfix still works after every change. + +# COMPATIBILITY +# +# The compatibility_level determines what default settings Postfix +# will use for main.cf and master.cf settings. These defaults will +# change over time. +# +# To avoid breaking things, Postfix will use backwards-compatible +# default settings and log where it uses those old backwards-compatible +# default settings, until the system administrator has determined +# if any backwards-compatible default settings need to be made +# permanent in main.cf or master.cf. +# +# When this review is complete, update the compatibility_level setting +# below as recommended in the RELEASE_NOTES file. +# +# The level below is what should be used with new (not upgrade) installs. +# +compatibility_level = 3.6 + +# SOFT BOUNCE +# +# The soft_bounce parameter provides a limited safety net for +# testing. When soft_bounce is enabled, mail will remain queued that +# would otherwise bounce. This parameter disables locally-generated +# bounces, and prevents the SMTP server from rejecting mail permanently +# (by changing 5xx replies into 4xx replies). However, soft_bounce +# is no cure for address rewriting mistakes or mail routing mistakes. +# +#soft_bounce = no + +# LOCAL PATHNAME INFORMATION +# +# The queue_directory specifies the location of the Postfix queue. +# This is also the root directory of Postfix daemons that run chrooted. +# See the files in examples/chroot-setup for setting up Postfix chroot +# environments on different UNIX systems. +# +queue_directory = /var/spool/postfix + +# The command_directory parameter specifies the location of all +# postXXX commands. +# +command_directory = /usr/sbin + +# The daemon_directory parameter specifies the location of all Postfix +# daemon programs (i.e. programs listed in the master.cf file). This +# directory must be owned by root. +# +daemon_directory = /usr/libexec/postfix + +# The data_directory parameter specifies the location of Postfix-writable +# data files (caches, random numbers). This directory must be owned +# by the mail_owner account (see below). +# +data_directory = /var/lib/postfix + +# QUEUE AND PROCESS OWNERSHIP +# +# The mail_owner parameter specifies the owner of the Postfix queue +# and of most Postfix daemon processes. Specify the name of a user +# account THAT DOES NOT SHARE ITS USER OR GROUP ID WITH OTHER ACCOUNTS +# AND THAT OWNS NO OTHER FILES OR PROCESSES ON THE SYSTEM. In +# particular, don't specify nobody or daemon. PLEASE USE A DEDICATED +# USER. +# +mail_owner = postfix + +# The default_privs parameter specifies the default rights used by +# the local delivery agent for delivery to external file or command. +# These rights are used in the absence of a recipient user context. +# DO NOT SPECIFY A PRIVILEGED USER OR THE POSTFIX OWNER. +# +#default_privs = nobody + +# INTERNET HOST AND DOMAIN NAMES +# +# The myhostname parameter specifies the internet hostname of this +# mail system. The default is to use the fully-qualified domain name +# from gethostname(). $myhostname is used as a default value for many +# other configuration parameters. +# +#myhostname = host.domain.tld +#myhostname = virtual.domain.tld + +# The mydomain parameter specifies the local internet domain name. +# The default is to use $myhostname minus the first component. +# $mydomain is used as a default value for many other configuration +# parameters. +# +#mydomain = domain.tld + +# SENDING MAIL +# +# The myorigin parameter specifies the domain that locally-posted +# mail appears to come from. The default is to append $myhostname, +# which is fine for small sites. If you run a domain with multiple +# machines, you should (1) change this to $mydomain and (2) set up +# a domain-wide alias database that aliases each user to +# user@that.users.mailhost. +# +# For the sake of consistency between sender and recipient addresses, +# myorigin also specifies the default domain name that is appended +# to recipient addresses that have no @domain part. +# +#myorigin = $myhostname +#myorigin = $mydomain + +# RECEIVING MAIL + +# The inet_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on. By default, +# the software claims all active interfaces on the machine. The +# parameter also controls delivery of mail to user@[ip.address]. +# +# See also the proxy_interfaces parameter, for network addresses that +# are forwarded to us via a proxy or network address translator. +# +# Note: you need to stop/start Postfix when this parameter changes. +# +#inet_interfaces = all +#inet_interfaces = $myhostname +#inet_interfaces = $myhostname, localhost + +# The proxy_interfaces parameter specifies the network interface +# addresses that this mail system receives mail on by way of a +# proxy or network address translation unit. This setting extends +# the address list specified with the inet_interfaces parameter. +# +# You must specify your proxy/NAT addresses when your system is a +# backup MX host for other domains, otherwise mail delivery loops +# will happen when the primary MX host is down. +# +#proxy_interfaces = +#proxy_interfaces = 1.2.3.4 + +# The mydestination parameter specifies the list of domains that this +# machine considers itself the final destination for. +# +# These domains are routed to the delivery agent specified with the +# local_transport parameter setting. By default, that is the UNIX +# compatible delivery agent that lookups all recipients in /etc/passwd +# and /etc/aliases or their equivalent. +# +# The default is $myhostname + localhost.$mydomain + localhost. On +# a mail domain gateway, you should also include $mydomain. +# +# Do not specify the names of virtual domains - those domains are +# specified elsewhere (see VIRTUAL_README). +# +# Do not specify the names of domains that this machine is backup MX +# host for. Specify those names via the relay_domains settings for +# the SMTP server, or use permit_mx_backup if you are lazy (see +# STANDARD_CONFIGURATION_README). +# +# The local machine is always the final destination for mail addressed +# to user@[the.net.work.address] of an interface that the mail system +# receives mail on (see the inet_interfaces parameter). +# +# Specify a list of host or domain names, /file/name or type:table +# patterns, separated by commas and/or whitespace. A /file/name +# pattern is replaced by its contents; a type:table is matched when +# a name matches a lookup key (the right-hand side is ignored). +# Continue long lines by starting the next line with whitespace. +# +# See also below, section "REJECTING MAIL FOR UNKNOWN LOCAL USERS". +# +#mydestination = $myhostname, localhost.$mydomain, localhost +#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain +#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, +# mail.$mydomain, www.$mydomain, ftp.$mydomain + +# REJECTING MAIL FOR UNKNOWN LOCAL USERS +# +# The local_recipient_maps parameter specifies optional lookup tables +# with all names or addresses of users that are local with respect +# to $mydestination, $inet_interfaces or $proxy_interfaces. +# +# If this parameter is defined, then the SMTP server will reject +# mail for unknown local users. This parameter is defined by default. +# +# To turn off local recipient checking in the SMTP server, specify +# local_recipient_maps = (i.e. empty). +# +# The default setting assumes that you use the default Postfix local +# delivery agent for local delivery. You need to update the +# local_recipient_maps setting if: +# +# - You define $mydestination domain recipients in files other than +# /etc/passwd, /etc/aliases, or the $virtual_alias_maps files. +# For example, you define $mydestination domain recipients in +# the $virtual_mailbox_maps files. +# +# - You redefine the local delivery agent in master.cf. +# +# - You redefine the "local_transport" setting in main.cf. +# +# - You use the "luser_relay", "mailbox_transport", or "fallback_transport" +# feature of the Postfix local delivery agent (see local(8)). +# +# Details are described in the LOCAL_RECIPIENT_README file. +# +# Beware: if the Postfix SMTP server runs chrooted, you probably have +# to access the passwd file via the proxymap service, in order to +# overcome chroot restrictions. The alternative, having a copy of +# the system passwd file in the chroot jail is just not practical. +# +# The right-hand side of the lookup tables is conveniently ignored. +# In the left-hand side, specify a bare username, an @domain.tld +# wild-card, or specify a user@domain.tld address. +# +#local_recipient_maps = unix:passwd.byname $alias_maps +#local_recipient_maps = proxy:unix:passwd.byname $alias_maps +#local_recipient_maps = + +# The unknown_local_recipient_reject_code specifies the SMTP server +# response code when a recipient domain matches $mydestination or +# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty +# and the recipient address or address local-part is not found. +# +# The default setting is 550 (reject mail) but it is safer to start +# with 450 (try again later) until you are certain that your +# local_recipient_maps settings are OK. +# +unknown_local_recipient_reject_code = 550 + +# TRUST AND RELAY CONTROL + +# The mynetworks parameter specifies the list of "trusted" SMTP +# clients that have more privileges than "strangers". +# +# In particular, "trusted" SMTP clients are allowed to relay mail +# through Postfix. See the smtpd_recipient_restrictions parameter +# in postconf(5). +# +# You can specify the list of "trusted" network addresses by hand +# or you can let Postfix do it for you (which is the default). +# +# By default (mynetworks_style = subnet), Postfix "trusts" SMTP +# clients in the same IP subnetworks as the local machine. +# On Linux, this works correctly only with interfaces specified +# with the "ifconfig" command. +# +# Specify "mynetworks_style = class" when Postfix should "trust" SMTP +# clients in the same IP class A/B/C networks as the local machine. +# Don't do this with a dialup site - it would cause Postfix to "trust" +# your entire provider's network. Instead, specify an explicit +# mynetworks list by hand, as described below. +# +# Specify "mynetworks_style = host" when Postfix should "trust" +# only the local machine. +# +#mynetworks_style = class +#mynetworks_style = subnet +#mynetworks_style = host + +# Alternatively, you can specify the mynetworks list by hand, in +# which case Postfix ignores the mynetworks_style setting. +# +# Specify an explicit list of network/netmask patterns, where the +# mask specifies the number of bits in the network part of a host +# address. +# +# You can also specify the absolute pathname of a pattern file instead +# of listing the patterns here. Specify type:table for table-based lookups +# (the value on the table right-hand side is not used). +# +mynetworks = 172.16.0.0/12 +#mynetworks = $config_directory/mynetworks +#mynetworks = hash:/etc/postfix/network_table + +# The relay_domains parameter restricts what destinations this system will +# relay mail to. See the smtpd_recipient_restrictions description in +# postconf(5) for detailed information. +# +# By default, Postfix relays mail +# - from "trusted" clients (IP address matches $mynetworks) to any destination, +# - from "untrusted" clients to destinations that match $relay_domains or +# subdomains thereof, except addresses with sender-specified routing. +# The default relay_domains value is $mydestination. +# +# In addition to the above, the Postfix SMTP server by default accepts mail +# that Postfix is final destination for: +# - destinations that match $inet_interfaces or $proxy_interfaces, +# - destinations that match $mydestination +# - destinations that match $virtual_alias_domains, +# - destinations that match $virtual_mailbox_domains. +# These destinations do not need to be listed in $relay_domains. +# +# Specify a list of hosts or domains, /file/name patterns or type:name +# lookup tables, separated by commas and/or whitespace. Continue +# long lines by starting the next line with whitespace. A file name +# is replaced by its contents; a type:name table is matched when a +# (parent) domain appears as lookup key. +# +# NOTE: Postfix will not automatically forward mail for domains that +# list this system as their primary or backup MX host. See the +# permit_mx_backup restriction description in postconf(5). +# +#relay_domains = $mydestination + +# INTERNET OR INTRANET + +# The relayhost parameter specifies the default host to send mail to +# when no entry is matched in the optional transport(5) table. When +# no relayhost is given, mail is routed directly to the destination. +# +# On an intranet, specify the organizational domain name. If your +# internal DNS uses no MX records, specify the name of the intranet +# gateway host instead. +# +# In the case of SMTP, specify a domain, host, host:port, [host]:port, +# [address] or [address]:port; the form [host] turns off MX lookups. +# +# If you're connected via UUCP, see also the default_transport parameter. +# +#relayhost = $mydomain +#relayhost = [gateway.my.domain] +#relayhost = [mailserver.isp.tld] +#relayhost = uucphost +#relayhost = [an.ip.add.ress] + +# REJECTING UNKNOWN RELAY USERS +# +# The relay_recipient_maps parameter specifies optional lookup tables +# with all addresses in the domains that match $relay_domains. +# +# If this parameter is defined, then the SMTP server will reject +# mail for unknown relay users. This feature is off by default. +# +# The right-hand side of the lookup tables is conveniently ignored. +# In the left-hand side, specify an @domain.tld wild-card, or specify +# a user@domain.tld address. +# +#relay_recipient_maps = hash:/etc/postfix/relay_recipients + +# INPUT RATE CONTROL +# +# The in_flow_delay configuration parameter implements mail input +# flow control. This feature is turned on by default, although it +# still needs further development (it's disabled on SCO UNIX due +# to an SCO bug). +# +# A Postfix process will pause for $in_flow_delay seconds before +# accepting a new message, when the message arrival rate exceeds the +# message delivery rate. With the default 100 SMTP server process +# limit, this limits the mail inflow to 100 messages a second more +# than the number of messages delivered per second. +# +# Specify 0 to disable the feature. Valid delays are 0..10. +# +#in_flow_delay = 1s + +# ADDRESS REWRITING +# +# The ADDRESS_REWRITING_README document gives information about +# address masquerading or other forms of address rewriting including +# username->Firstname.Lastname mapping. + +# ADDRESS REDIRECTION (VIRTUAL DOMAIN) +# +# The VIRTUAL_README document gives information about the many forms +# of domain hosting that Postfix supports. + +# "USER HAS MOVED" BOUNCE MESSAGES +# +# See the discussion in the ADDRESS_REWRITING_README document. + +# TRANSPORT MAP +# +# See the discussion in the ADDRESS_REWRITING_README document. + +# ALIAS DATABASE +# +# The alias_maps parameter specifies the list of alias databases used +# by the local delivery agent. The default list is system dependent. +# +# On systems with NIS, the default is to search the local alias +# database, then the NIS alias database. See aliases(5) for syntax +# details. +# +# If you change the alias database, run "postalias /etc/aliases" (or +# wherever your system stores the mail alias file), or simply run +# "newaliases" to build the necessary DBM or DB file. +# +# It will take a minute or so before changes become visible. Use +# "postfix reload" to eliminate the delay. +# +#alias_maps = dbm:/etc/aliases +#alias_maps = hash:/etc/aliases +#alias_maps = hash:/etc/aliases, nis:mail.aliases +#alias_maps = netinfo:/aliases + +# The alias_database parameter specifies the alias database(s) that +# are built with "newaliases" or "sendmail -bi". This is a separate +# configuration parameter, because alias_maps (see above) may specify +# tables that are not necessarily all under control by Postfix. +# +#alias_database = dbm:/etc/aliases +#alias_database = dbm:/etc/mail/aliases +#alias_database = hash:/etc/aliases +#alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases + +# ADDRESS EXTENSIONS (e.g., user+foo) +# +# The recipient_delimiter parameter specifies the separator between +# user names and address extensions (user+foo). See canonical(5), +# local(8), relocated(5) and virtual(5) for the effects this has on +# aliases, canonical, virtual, relocated and .forward file lookups. +# Basically, the software tries user+foo and .forward+foo before +# trying user and .forward. +# +#recipient_delimiter = + + +# DELIVERY TO MAILBOX +# +# The home_mailbox parameter specifies the optional pathname of a +# mailbox file relative to a user's home directory. The default +# mailbox file is /var/spool/mail/user or /var/mail/user. Specify +# "Maildir/" for qmail-style delivery (the / is required). +# +#home_mailbox = Mailbox +#home_mailbox = Maildir/ + +# The mail_spool_directory parameter specifies the directory where +# UNIX-style mailboxes are kept. The default setting depends on the +# system type. +# +#mail_spool_directory = /var/mail +#mail_spool_directory = /var/spool/mail + +# The mailbox_command parameter specifies the optional external +# command to use instead of mailbox delivery. The command is run as +# the recipient with proper HOME, SHELL and LOGNAME environment settings. +# Exception: delivery for root is done as $default_user. +# +# Other environment variables of interest: USER (recipient username), +# EXTENSION (address extension), DOMAIN (domain part of address), +# and LOCAL (the address localpart). +# +# Unlike other Postfix configuration parameters, the mailbox_command +# parameter is not subjected to $parameter substitutions. This is to +# make it easier to specify shell syntax (see example below). +# +# Avoid shell meta characters because they will force Postfix to run +# an expensive shell process. Procmail alone is expensive enough. +# +# IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN +# ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER. +# +#mailbox_command = /some/where/procmail +#mailbox_command = /some/where/procmail -a "$EXTENSION" + +# The mailbox_transport specifies the optional transport in master.cf +# to use after processing aliases and .forward files. This parameter +# has precedence over the mailbox_command, fallback_transport and +# luser_relay parameters. +# +# Specify a string of the form transport:nexthop, where transport is +# the name of a mail delivery transport defined in master.cf. The +# :nexthop part is optional. For more details see the sample transport +# configuration file. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must update the "local_recipient_maps" setting in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +# Cyrus IMAP over LMTP. Specify ``lmtpunix cmd="lmtpd" +# listen="/var/imap/socket/lmtp" prefork=0'' in cyrus.conf. +#mailbox_transport = lmtp:unix:/var/imap/socket/lmtp +# +# Cyrus IMAP via command line. Uncomment the "cyrus...pipe" and +# subsequent line in master.cf. +#mailbox_transport = cyrus + +# The fallback_transport specifies the optional transport in master.cf +# to use for recipients that are not found in the UNIX passwd database. +# This parameter has precedence over the luser_relay parameter. +# +# Specify a string of the form transport:nexthop, where transport is +# the name of a mail delivery transport defined in master.cf. The +# :nexthop part is optional. For more details see the sample transport +# configuration file. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must update the "local_recipient_maps" setting in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +#fallback_transport = lmtp:unix:/file/name +#fallback_transport = cyrus +#fallback_transport = + +# The luser_relay parameter specifies an optional destination address +# for unknown recipients. By default, mail for unknown@$mydestination, +# unknown@[$inet_interfaces] or unknown@[$proxy_interfaces] is returned +# as undeliverable. +# +# The following expansions are done on luser_relay: $user (recipient +# username), $shell (recipient shell), $home (recipient home directory), +# $recipient (full recipient address), $extension (recipient address +# extension), $domain (recipient domain), $local (entire recipient +# localpart), $recipient_delimiter. Specify ${name?value} or +# ${name:value} to expand value only when $name does (does not) exist. +# +# luser_relay works only for the default Postfix local delivery agent. +# +# NOTE: if you use this feature for accounts not in the UNIX password +# file, then you must specify "local_recipient_maps =" (i.e. empty) in +# the main.cf file, otherwise the SMTP server will reject mail for +# non-UNIX accounts with "User unknown in local recipient table". +# +#luser_relay = $user@other.host +#luser_relay = $local@other.host +#luser_relay = admin+$local + +# JUNK MAIL CONTROLS +# +# The controls listed here are only a very small subset. The file +# SMTPD_ACCESS_README provides an overview. + +# The header_checks parameter specifies an optional table with patterns +# that each logical message header is matched against, including +# headers that span multiple physical lines. +# +# By default, these patterns also apply to MIME headers and to the +# headers of attached messages. With older Postfix versions, MIME and +# attached message headers were treated as body text. +# +# For details, see "man header_checks". +# +#header_checks = regexp:/etc/postfix/header_checks + +# FAST ETRN SERVICE +# +# Postfix maintains per-destination logfiles with information about +# deferred mail, so that mail can be flushed quickly with the SMTP +# "ETRN domain.tld" command, or by executing "sendmail -qRdomain.tld". +# See the ETRN_README document for a detailed description. +# +# The fast_flush_domains parameter controls what destinations are +# eligible for this service. By default, they are all domains that +# this server is willing to relay mail to. +# +#fast_flush_domains = $relay_domains + +# SHOW SOFTWARE VERSION OR NOT +# +# The smtpd_banner parameter specifies the text that follows the 220 +# code in the SMTP server's greeting banner. Some people like to see +# the mail version advertised. By default, Postfix shows no version. +# +# You MUST specify $myhostname at the start of the text. That is an +# RFC requirement. Postfix itself does not care. +# +#smtpd_banner = $myhostname ESMTP $mail_name +#smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) + +# PARALLEL DELIVERY TO THE SAME DESTINATION +# +# How many parallel deliveries to the same user or domain? With local +# delivery, it does not make sense to do massively parallel delivery +# to the same user, because mailbox updates must happen sequentially, +# and expensive pipelines in .forward files can cause disasters when +# too many are run at the same time. With SMTP deliveries, 10 +# simultaneous connections to the same domain could be sufficient to +# raise eyebrows. +# +# Each message delivery transport has its XXX_destination_concurrency_limit +# parameter. The default is $default_destination_concurrency_limit for +# most delivery transports. For the local delivery agent the default is 2. + +#local_destination_concurrency_limit = 2 +#default_destination_concurrency_limit = 20 + +# DEBUGGING CONTROL +# +# The debug_peer_level parameter specifies the increment in verbose +# logging level when an SMTP client or server host name or address +# matches a pattern in the debug_peer_list parameter. +# +debug_peer_level = 2 + +# The debug_peer_list parameter specifies an optional list of domain +# or network patterns, /file/name patterns or type:name tables. When +# an SMTP client or server host name or address matches a pattern, +# increase the verbose logging level by the amount specified in the +# debug_peer_level parameter. +# +#debug_peer_list = 127.0.0.1 +#debug_peer_list = some.domain + +# The debugger_command specifies the external command that is executed +# when a Postfix daemon program is run with the -D option. +# +# Use "command .. & sleep 5" so that the debugger can attach before +# the process marches on. If you use an X-based debugger, be sure to +# set up your XAUTHORITY environment variable before starting Postfix. +# +debugger_command = + PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin + ddd $daemon_directory/$process_name $process_id & sleep 5 + +# If you can't use X, use this to capture the call stack when a +# daemon crashes. The result is in a file in the configuration +# directory, and is named after the process name and the process ID. +# +# debugger_command = +# PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont; +# echo where) | gdb $daemon_directory/$process_name $process_id 2>&1 +# >$config_directory/$process_name.$process_id.log & sleep 5 +# +# Another possibility is to run gdb under a detached screen session. +# To attach to the screen session, su root and run "screen -r +# " where uniquely matches one of the detached +# sessions (from "screen -list"). +# +# debugger_command = +# PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; screen +# -dmS $process_name gdb $daemon_directory/$process_name +# $process_id & sleep 1 + +# INSTALL-TIME CONFIGURATION INFORMATION +# +# The following parameters are used when installing a new Postfix version. +# +# sendmail_path: The full pathname of the Postfix sendmail command. +# This is the Sendmail-compatible mail posting interface. +# +sendmail_path = /usr/sbin/sendmail + +# newaliases_path: The full pathname of the Postfix newaliases command. +# This is the Sendmail-compatible command to build alias databases. +# +newaliases_path = /usr/bin/newaliases + +# mailq_path: The full pathname of the Postfix mailq command. This +# is the Sendmail-compatible mail queue listing command. +# +mailq_path = /usr/bin/mailq + +# setgid_group: The group for mail submission and queue management +# commands. This must be a group name with a numerical group ID that +# is not shared with other accounts, not even with the Postfix account. +# +setgid_group = postdrop + +# html_directory: The location of the Postfix HTML documentation. +# +html_directory = no + +# manpage_directory: The location of the Postfix on-line manual pages. +# +manpage_directory = /usr/share/man + +# sample_directory: The location of the Postfix sample configuration files. +# This parameter is obsolete as of Postfix 2.1. +# +sample_directory = /etc/postfix + +# readme_directory: The location of the Postfix README files. +# +readme_directory = /usr/share/doc/postfix/readme +inet_protocols = ipv4 +meta_directory = /etc/postfix +shlib_directory = /usr/lib/postfix + +smtpd_tls_security_level=may +smtpd_tls_auth_only = yes + +#maillog_file=/dev/stdout + +smtpd_tls_cert_file = /etc/ssl/cert-data/tls-server.com-chain.crt +smtpd_tls_key_file = /etc/ssl/cert-data/tls-server.com.key \ No newline at end of file diff --git a/evaluation-servers/postfix/master.cf b/evaluation-servers/postfix/master.cf new file mode 100644 index 0000000..19b73b0 --- /dev/null +++ b/evaluation-servers/postfix/master.cf @@ -0,0 +1,139 @@ +# +# Postfix master process configuration file. For details on the format +# of the file, see the master(5) manual page (command: "man 5 master" or +# on-line: http://www.postfix.org/master.5.html). +# +# Do not forget to execute "postfix reload" after editing this file. +# +# ========================================================================== +# service type private unpriv chroot wakeup maxproc command + args +# (yes) (yes) (no) (never) (100) +# ========================================================================== +465 inet n - n - - smtpd +#smtp inet n - n - 1 postscreen +#smtpd pass - - n - - smtpd +#dnsblog unix - - n - 0 dnsblog +#tlsproxy unix - - n - 0 tlsproxy +# Choose one: enable submission for loopback clients only, or for any client. +#127.0.0.1:submission inet n - n - - smtpd +submission inet n - n - - smtpd + -o syslog_name=postfix/submission + -o smtpd_tls_security_level=encrypt + -o smtpd_sasl_auth_enable=yes +# -o syslog_name=postfix/submission +# -o smtpd_sasl_auth_enable=yes +# -o smtpd_tls_auth_only=yes +# -o smtpd_reject_unlisted_recipient=no +# -o smtpd_client_restrictions=$mua_client_restrictions +# -o smtpd_helo_restrictions=$mua_helo_restrictions +# -o smtpd_sender_restrictions=$mua_sender_restrictions +# -o smtpd_recipient_restrictions= +# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject +# -o milter_macro_daemon_name=ORIGINATING +# Choose one: enable smtps for loopback clients only, or for any client. +#smtps inet n - n - - smtpd +#smtps inet n - n - - smtpd +# -o syslog_name=postfix/smtps +# -o smtpd_tls_wrappermode=yes +# -o smtpd_sasl_auth_enable=yes +# -o smtpd_reject_unlisted_recipient=no +# -o smtpd_client_restrictions=$mua_client_restrictions +# -o smtpd_helo_restrictions=$mua_helo_restrictions +# -o smtpd_sender_restrictions=$mua_sender_restrictions +# -o smtpd_recipient_restrictions= +# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject +# -o milter_macro_daemon_name=ORIGINATING +#628 inet n - n - - qmqpd +pickup unix n - n 60 1 pickup +cleanup unix n - n - 0 cleanup +qmgr unix n - n 300 1 qmgr +#qmgr unix n - n 300 1 oqmgr +tlsmgr unix - - n 1000? 1 tlsmgr +rewrite unix - - n - - trivial-rewrite +bounce unix - - n - 0 bounce +defer unix - - n - 0 bounce +trace unix - - n - 0 bounce +verify unix - - n - 1 verify +flush unix n - n 1000? 0 flush +proxymap unix - - n - - proxymap +proxywrite unix - - n - 1 proxymap +smtp unix - - n - - smtp +relay unix - - n - - smtp + -o syslog_name=postfix/$service_name +# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 +showq unix n - n - - showq +error unix - - n - - error +retry unix - - n - - error +discard unix - - n - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - n - - lmtp +anvil unix - - n - 1 anvil +scache unix - - n - 1 scache +postlog unix-dgram n - n - 1 postlogd +# +# ==================================================================== +# Interfaces to non-Postfix software. Be sure to examine the manual +# pages of the non-Postfix software to find out what options it wants. +# +# Many of the following services use the Postfix pipe(8) delivery +# agent. See the pipe(8) man page for information about ${recipient} +# and other message envelope options. +# ==================================================================== +# +# maildrop. See the Postfix MAILDROP_README file for details. +# Also specify in main.cf: maildrop_destination_recipient_limit=1 +# +#maildrop unix - n n - - pipe +# flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient} +# +# ==================================================================== +# +# Recent Cyrus versions can use the existing "lmtp" master.cf entry. +# +# Specify in cyrus.conf: +# lmtp cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4 +# +# Specify in main.cf one or more of the following: +# mailbox_transport = lmtp:inet:localhost +# virtual_transport = lmtp:inet:localhost +# +# ==================================================================== +# +# Cyrus 2.1.5 (Amos Gouaux) +# Also specify in main.cf: cyrus_destination_recipient_limit=1 +# +#cyrus unix - n n - - pipe +# flags=DRX user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user} +# +# ==================================================================== +# +# Old example of delivery via Cyrus. +# +#old-cyrus unix - n n - - pipe +# flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user} +# +# ==================================================================== +# +# See the Postfix UUCP_README file for configuration details. +# +#uucp unix - n n - - pipe +# flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) +# +# ==================================================================== +# +# Other external delivery methods. +# +#ifmail unix - n n - - pipe +# flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) +# +#bsmtp unix - n n - - pipe +# flags=Fq. user=bsmtp argv=/usr/sbin/bsmtp -f $sender $nexthop $recipient +# +#scalemail-backend unix - n n - 2 pipe +# flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store +# ${nexthop} ${user} ${extension} +# +#mailman unix - n n - - pipe +# flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py +# ${nexthop} ${user} diff --git a/evaluation-servers/postfix/run.sh b/evaluation-servers/postfix/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/postfix/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/proftpd/Dockerfile b/evaluation-servers/proftpd/Dockerfile new file mode 100644 index 0000000..817c416 --- /dev/null +++ b/evaluation-servers/proftpd/Dockerfile @@ -0,0 +1,22 @@ +FROM tls-baseimage +ARG VERSION=1.3.8rc2 +ARG TLS_SERVER_NAME=tls-server.com +RUN apk add openssl-dev +WORKDIR /build +RUN wget https://github.com/proftpd/proftpd/archive/refs/tags/v${VERSION}.tar.gz +RUN tar -xvf v${VERSION}.tar.gz +WORKDIR /build/proftpd-${VERSION} +RUN ./configure --with-modules=mod_tls --with-virtualhosts +RUN make +RUN mv proftpd /bin/ + +#fix errors on start +RUN mkdir /usr/local/var/ + +RUN echo "tls-server.com" > /etc/hostname +RUN echo "tls-server.com 127.0.0.1" > /etc/hosts + +ADD proftpd.conf /etc/proftpd.conf + + +CMD ["proftpd", "-d", "10", "-n" , "-c" , "/etc/proftpd.conf"] \ No newline at end of file diff --git a/evaluation-servers/proftpd/build.sh b/evaluation-servers/proftpd/build.sh new file mode 100755 index 0000000..c476f4e --- /dev/null +++ b/evaluation-servers/proftpd/build.sh @@ -0,0 +1 @@ +docker build . -t tls-proftpd \ No newline at end of file diff --git a/evaluation-servers/proftpd/docker-compose.yml b/evaluation-servers/proftpd/docker-compose.yml new file mode 100644 index 0000000..a4977c1 --- /dev/null +++ b/evaluation-servers/proftpd/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + tls-server.com: + image: tls-proftpd + scanner: + image: tlsscanner + depends_on: + - tls-server.com + command: [ "-connect", "tls-server.com:21", "-server_name", "tls-server.com", "-starttls", "ftp", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/proftpd/proftpd.conf b/evaluation-servers/proftpd/proftpd.conf new file mode 100644 index 0000000..635d479 --- /dev/null +++ b/evaluation-servers/proftpd/proftpd.conf @@ -0,0 +1,55 @@ +ScoreboardFile /dev/null +ServerType standalone +UseReverseDNS off + + TLSEngine on + TLSLog /usr/local/var/tls.log + TLSProtocol TLSv1.2 + TLSRequired on + TLSRSACertificateFile /etc/ssl/cert-data/tls-server.com-chain.crt + TLSRSACertificateKeyFile /etc/ssl/cert-data/tls-server.com.key + TLSVerifyClient off + TLSRenegotiate none + TLSNextProtocol on + TLSOptions StdEnvVars + + # This is a basic ProFTPD configuration file (rename it to + # 'proftpd.conf' for actual use. It establishes a single server + # and a single anonymous login. It assumes that you have a user/group + # "nobody" and "ftp" for normal operation and anon. + + ServerName "tls-server.com" + + DefaultServer on + + # Port 21 is the standard FTP port. + Port 21 + + # Bar use of SITE CHMOD by default + + DenyAll + + + # A basic anonymous configuration, no upload directories. If you do not + # want anonymous users, simply delete this entire section. + + User ftp + Group ftp + + # We want clients to be able to login with "anonymous" as well as "ftp" + UserAlias anonymous ftp + + # Limit the maximum number of anonymous logins + MaxClients 10 + + # We want 'welcome.msg' displayed at login, and '.message' displayed + # in each newly chdired directory. + DisplayLogin welcome.msg + DisplayChdir .message + + # Limit WRITE everywhere in the anonymous chroot + + DenyAll + + + diff --git a/evaluation-servers/proftpd/run.sh b/evaluation-servers/proftpd/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/proftpd/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/pure-ftpd/Dockerfile b/evaluation-servers/pure-ftpd/Dockerfile new file mode 100644 index 0000000..c2f8d2d --- /dev/null +++ b/evaluation-servers/pure-ftpd/Dockerfile @@ -0,0 +1,17 @@ +FROM tls-baseimage +ARG VERSION=1.0.49 +RUN apk add openssl-dev +WORKDIR /build +RUN wget https://github.com/jedisct1/pure-ftpd/releases/download/${VERSION}/pure-ftpd-${VERSION}.tar.gz +RUN tar -xvf pure-ftpd-${VERSION}.tar.gz +WORKDIR /build/pure-ftpd-${VERSION} +RUN ./configure --with-tls --without-capabilities +RUN make +RUN mv src/pure-ftpd /bin/ + +RUN mkdir /etc/ssl/private + +RUN cat /etc/ssl/cert-data/tls-server.com.key >> /etc/ssl/private/pure-ftpd.pem +RUN cat /etc/ssl/cert-data/tls-server.com-chain.crt >> /etc/ssl/private/pure-ftpd.pem + +CMD ["pure-ftpd", "-Y" , "2"] \ No newline at end of file diff --git a/evaluation-servers/pure-ftpd/build.sh b/evaluation-servers/pure-ftpd/build.sh new file mode 100755 index 0000000..a283f35 --- /dev/null +++ b/evaluation-servers/pure-ftpd/build.sh @@ -0,0 +1 @@ +docker build . -t tls-pureftpd \ No newline at end of file diff --git a/evaluation-servers/pure-ftpd/docker-compose.yml b/evaluation-servers/pure-ftpd/docker-compose.yml new file mode 100644 index 0000000..6dd682e --- /dev/null +++ b/evaluation-servers/pure-ftpd/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + pureftpd: + image: tls-pureftpd + scanner: + image: tlsscanner + depends_on: + - pureftpd + command: [ "-connect", "pureftpd:21", "-server_name", "tls-server.com", "-starttls", "ftp", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/pure-ftpd/run.sh b/evaluation-servers/pure-ftpd/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/pure-ftpd/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/sendmail/Dockerfile b/evaluation-servers/sendmail/Dockerfile new file mode 100644 index 0000000..fb5ba5a --- /dev/null +++ b/evaluation-servers/sendmail/Dockerfile @@ -0,0 +1,39 @@ +FROM tls-baseimagedebian + +RUN apt-get update && apt-get install -yq sendmail openssl libsasl2-2 sasl2-bin + +RUN echo "tls-server.com" >> /etc/hostname +RUN echo "tls-server.com 127.0.0.1" >> /etc/hosts + +RUN echo "include(\`/etc/mail/tls/starttls.m4')dnl" >> /etc/mail/sendmail.mc +RUN echo "include(\`/etc/mail/tls/starttls.m4')dnl" >> /etc/mail/submit.mc + +RUN echo "define(\`confCACERT_PATH', \`/etc/ssl/cert-data/')dnl" >> /etc/mail/sendmail.mc +RUN echo "define(\`confLOG_LEVEL', \`14')" >> /etc/mail/sendmail.mc + +RUN echo "define(\`SMART_HOST', \`tls-server.com')dnl" >> /etc/mail/sendmail.mc + +RUN echo "define(\`confSERVER_CERT', \`/etc/ssl/cert-data/tls-server.com-chain.crt')dnl" >> /etc/mail/sendmail.mc +RUN echo "define(\`confSERVER_KEY', \`/etc/ssl/cert-data/tls-server.com.key')dnl" >> /etc/mail/sendmail.mc +RUN echo "define(\`confCLIENT_CERT', \`/etc/ssl/cert-data/tls-server.com-chain.crt')dnl" >> /etc/mail/sendmail.mc +RUN echo "define(\`confCLIENT_KEY', \`/etc/ssl/cert-data/tls-server.com.key')dnl" >> /etc/mail/sendmail.mc + +RUN echo "GreetPause:192.16 0" >> /etc/mail/access +RUN echo "ClientRate:172.16 0" >> /etc/mail/access +RUN echo "GreetPause:172.16 0" >> /etc/mail/access + +RUN sed -i 's/127.0.0.1/0.0.0.0/' /etc/mail/sendmail.mc + +RUN chmod 600 /etc/ssl/cert-data/* + +RUN yes 'y' | sendmailconfig + +RUN chmod 777 /etc/ssl/cert-data/tls-server.com-chain.crt +RUN chmod 777 /etc/ssl/cert-data/tls-server.com.key + +USER sendmail +#CMD ["exim", "-bd", "-d-all+pid", "-q30m"] +ADD start.sh /root/ +RUN chmod +x /root/start.sh +CMD ["/root/start.sh"] +#CMD ["sendmail", "-bD", "-d0.14"] \ No newline at end of file diff --git a/evaluation-servers/sendmail/build.sh b/evaluation-servers/sendmail/build.sh new file mode 100755 index 0000000..5e14e72 --- /dev/null +++ b/evaluation-servers/sendmail/build.sh @@ -0,0 +1 @@ +docker build . -t tls-sendmail diff --git a/evaluation-servers/sendmail/docker-compose.yml b/evaluation-servers/sendmail/docker-compose.yml new file mode 100644 index 0000000..50412ab --- /dev/null +++ b/evaluation-servers/sendmail/docker-compose.yml @@ -0,0 +1,16 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + tls-sendmail: + image: tls-sendmail + hostname: tls-server.com + container_name: tls-server.com + scanner: + image: tlsscanner + depends_on: + - tls-sendmail + command: [ "-connect", "tls-server.com:25", "-server_name", "tls-server.com", "-starttls", "SMTP", "-scanDetail", "QUICK"] + restart: always \ No newline at end of file diff --git a/evaluation-servers/sendmail/run.sh b/evaluation-servers/sendmail/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/sendmail/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/sendmail/start.sh b/evaluation-servers/sendmail/start.sh new file mode 100644 index 0000000..70f6ce5 --- /dev/null +++ b/evaluation-servers/sendmail/start.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# /etc/init.d/sendmail start +# echo Started sendmail +# + +cat /etc/hostname +echo "tls-server" > /etc/hostname +echo "127.0.0.1 tls-server.com tls-server localdev localhost" > /etc/hosts + +touch /var/log/mail.info +sendmail -bD -d0.14 \ No newline at end of file diff --git a/evaluation-servers/vsftpd/Dockerfile b/evaluation-servers/vsftpd/Dockerfile new file mode 100644 index 0000000..3c64415 --- /dev/null +++ b/evaluation-servers/vsftpd/Dockerfile @@ -0,0 +1,25 @@ +FROM tls-baseimage +ARG VERSION=3.0.5 +RUN apk add vsftpd +#RUN apk add openssl-dev +#WORKDIR /build +#RUN wget https://security.appspot.com/downloads/vsftpd-${VERSION}.tar.gz +#RUN tar -xvf vsftpd-${VERSION}.tar.gz +#WORKDIR /build/vsftpd-${VERSION} +#RUN ./configure --with-modules=mod_tls --with-virtualhosts +#RUN make +#RUN mv proftpd /bin/ + +RUN mkdir /var/ftp +RUN chmod 777 /var/ftp + +RUN echo "tls-server.com" > /etc/hostname +RUN echo "tls-server.com 127.0.0.1" > /etc/hosts + +ADD vsftpd.conf /etc/vsftpd/vsftpd.conf + +ADD start.sh /root/ +RUN chmod +x /root/start.sh +CMD ["/root/start.sh"] + +#CMD ["/usr/sbin/vsftpd", "/etc/vsftpd/vsftpd.conf"] \ No newline at end of file diff --git a/evaluation-servers/vsftpd/build.sh b/evaluation-servers/vsftpd/build.sh new file mode 100755 index 0000000..9c93b01 --- /dev/null +++ b/evaluation-servers/vsftpd/build.sh @@ -0,0 +1 @@ +docker build . -t tls-vsftpd \ No newline at end of file diff --git a/evaluation-servers/vsftpd/docker-compose.yml b/evaluation-servers/vsftpd/docker-compose.yml new file mode 100644 index 0000000..d4d4191 --- /dev/null +++ b/evaluation-servers/vsftpd/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.9" +networks: + default: + name: tls-network + internal: true +services: + vsftpd: + image: tls-vsftpd + scanner: + image: tlsscanner + depends_on: + - vsftpd + command: [ "-connect", "vsftpd:21", "-server_name", "tls-server.com", "-starttls", "ftp", "-scanDetail", "QUICK"] \ No newline at end of file diff --git a/evaluation-servers/vsftpd/run.sh b/evaluation-servers/vsftpd/run.sh new file mode 100755 index 0000000..36ab020 --- /dev/null +++ b/evaluation-servers/vsftpd/run.sh @@ -0,0 +1,2 @@ +./build.sh +docker-compose up --remove-orphans diff --git a/evaluation-servers/vsftpd/start.sh b/evaluation-servers/vsftpd/start.sh new file mode 100644 index 0000000..f98b954 --- /dev/null +++ b/evaluation-servers/vsftpd/start.sh @@ -0,0 +1,7 @@ +#!/bin/bash +#echo test +touch /var/log/vsftpd.log +/usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf & tail -f /var/log/vsftpd.log +#rc-update add vsftpd default +#rc-service vsftpd restart +#tail -f /var/log/vsftpd.log \ No newline at end of file diff --git a/evaluation-servers/vsftpd/vsftpd.conf b/evaluation-servers/vsftpd/vsftpd.conf new file mode 100644 index 0000000..421e79a --- /dev/null +++ b/evaluation-servers/vsftpd/vsftpd.conf @@ -0,0 +1,136 @@ +# Example config file /etc/vsftpd.conf +# +# The default compiled in settings are fairly paranoid. This sample file +# loosens things up a bit, to make the ftp daemon more usable. +# Please see vsftpd.conf.5 for all compiled in defaults. +# +# READ THIS: This example file is NOT an exhaustive list of vsftpd options. +# Please read the vsftpd.conf.5 manual page to get a full idea of vsftpd's +# capabilities. +# +# Allow anonymous FTP? (Beware - allowed by default if you comment this out). +anonymous_enable=YES +# +# Uncomment this to allow local users to log in. +#local_enable=YES +# +# Uncomment this to enable any form of FTP write command. +#write_enable=YES +# +# Default umask for local users is 077. You may wish to change this to 022, +# if your users expect that (022 is used by most other ftpd's) +#local_umask=022 +# +# Uncomment this to allow the anonymous FTP user to upload files. This only +# has an effect if the above global write enable is activated. Also, you will +# obviously need to create a directory writable by the FTP user. +#anon_upload_enable=YES +# +# Uncomment this if you want the anonymous FTP user to be able to create +# new directories. +#anon_mkdir_write_enable=YES +# +# Activate directory messages - messages given to remote users when they +# go into a certain directory. +dirmessage_enable=YES +# +# Activate logging of uploads/downloads. +xferlog_enable=YES +# +# Make sure PORT transfer connections originate from port 20 (ftp-data). +connect_from_port_20=YES +# +# If you want, you can arrange for uploaded anonymous files to be owned by +# a different user. Note! Using "root" for uploaded files is not +# recommended! +#chown_uploads=YES +#chown_username=whoever +# +# You may override where the log file goes if you like. The default is shown +# below. +#xferlog_file=/var/log/vsftpd.log +# +# If you want, you can have your log file in standard ftpd xferlog format. +# Note that the default log file location is /var/log/xferlog in this case. +#xferlog_std_format=YES +# +# You may change the default value for timing out an idle session. +#idle_session_timeout=600 +# +# You may change the default value for timing out a data connection. +#data_connection_timeout=120 +# +# It is recommended that you define on your system a unique user which the +# ftp server can use as a totally isolated and unprivileged user. +#nopriv_user=ftpsecure +# +# Enable this and the server will recognise asynchronous ABOR requests. Not +# recommended for security (the code is non-trivial). Not enabling it, +# however, may confuse older FTP clients. +#async_abor_enable=YES +# +# By default the server will pretend to allow ASCII mode but in fact ignore +# the request. Turn on the below options to have the server actually do ASCII +# mangling on files when in ASCII mode. +# Beware that on some FTP servers, ASCII support allows a denial of service +# attack (DoS) via the command "SIZE /big/file" in ASCII mode. vsftpd +# predicted this attack and has always been safe, reporting the size of the +# raw file. +# ASCII mangling is a horrible feature of the protocol. +#ascii_upload_enable=YES +#ascii_download_enable=YES +# +# You may fully customise the login banner string: +#ftpd_banner=Welcome to blah FTP service. +# +# You may specify a file of disallowed anonymous e-mail addresses. Apparently +# useful for combatting certain DoS attacks. +#deny_email_enable=YES +# (default follows) +#banned_email_file=/etc/vsftpd.banned_emails +# +# You may specify an explicit list of local users to chroot() to their home +# directory. If chroot_local_user is YES, then this list becomes a list of +# users to NOT chroot(). +# (Warning! chroot'ing can be very dangerous. If using chroot, make sure that +# the user does not have write access to the top level directory within the +# chroot) +#chroot_local_user=YES +#chroot_list_enable=YES +# (default follows) +#chroot_list_file=/etc/vsftpd.chroot_list +allow_writeable_chroot=yes +# +# You may activate the "-R" option to the builtin ls. This is disabled by +# default to avoid remote users being able to cause excessive I/O on large +# sites. However, some broken FTP clients such as "ncftp" and "mirror" assume +# the presence of the "-R" option, so there is a strong case for enabling it. +#ls_recurse_enable=YES +# +# When "listen" directive is enabled, vsftpd runs in standalone mode and +# listens on IPv4 sockets. This directive cannot be used in conjunction +# with the listen_ipv6 directive. +listen=YES +# +# This directive enables listening on IPv6 sockets. To listen on IPv4 and IPv6 +# sockets, you must run two copies of vsftpd with two configuration files. +# Make sure, that one of the listen options is commented !! +#listen_ipv6=YES + +# Set own PAM service name to detect authentication settings specified +# for vsftpd by the system package. +#pam_service_name=vsftpd + + +seccomp_sandbox=NO +local_root=/var/ftp +anon_root=/var/ftp + + +rsa_cert_file=/etc/ssl/cert-data/tls-server.com-chain.crt +rsa_private_key_file=/etc/ssl/cert-data/tls-server.com.key +ssl_enable=yes +allow_anon_ssl=yes +#debug_ssl=yes +#ssl_ciphers=TLSv1.2 +ssl_sni_hostname=tls-server.com \ No newline at end of file