diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2022-07-13 12:06:19 +0200 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2023-01-17 22:03:00 +0100 |
commit | ac4c7390a36b15503cff0ce2ed78ba816c5c38e6 (patch) | |
tree | 14ec0b270bafb8ccd3babb2917d455764aa769c2 | |
parent | 5e313f43f956dd3a94c65529bad7a90d63c5a0e5 (diff) |
Added TLS proxy support.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r-- | .github/workflows/build.yml | 4 | ||||
-rw-r--r-- | CMakeLists.txt | 29 | ||||
-rw-r--r-- | nDPIsrvd.c | 211 | ||||
-rwxr-xr-x | scripts/generate-tls-ca.sh | 32 | ||||
-rwxr-xr-x | scripts/generate-tls-cert.sh | 17 | ||||
-rw-r--r-- | tls-cli.c | 167 | ||||
-rw-r--r-- | tls-srv.c | 146 |
7 files changed, 599 insertions, 7 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c273052f0..35d5e626d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,6 +51,7 @@ jobs: sudo apt-get update sudo apt-get install autoconf automake cmake libtool pkg-config gettext libjson-c-dev flex bison libpcap-dev zlib1g-dev sudo apt-get install ${{ matrix.compiler }} lcov iproute2 + sudo apt-get install rpm alien - name: Install Ubuntu Prerequisites (libgcrypt) if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.ndpid_gcrypt, '-DNDPI_WITH_GCRYPT=ON') run: | @@ -93,6 +94,9 @@ jobs: - name: CPack DEB run: | cd ./build && cpack -G DEB && sudo dpkg -i nDPId-*.deb && cd .. + - name: CPack RPM + run: | + cd ./build && cpack -G RPM - name: systemd test if: startsWith(matrix.os, 'ubuntu-latest') && startsWith(matrix.compiler, 'default-cc') run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index ecb1dece3..a770388e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,13 +10,20 @@ endif() set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) find_package(PkgConfig REQUIRED) +set(CMAKE_PROJECT_HOMEPAGE_URL "https://github.com/utoni/nDPId") +set(CPACK_PACKAGE_NAME "nDPId") set(CPACK_PACKAGE_CONTACT "toni@impl.cc") -set(CPACK_DEBIAN_PACKAGE_NAME "nDPId") -set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Tiny nDPI based deep packet inspection daemons / toolkit.") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") +set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") set(CPACK_PACKAGE_VERSION_MAJOR 1) set(CPACK_PACKAGE_VERSION_MINOR 5) set(CPACK_PACKAGE_VERSION_PATCH 0) +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +set(CPACK_RPM_PACKAGE_LICENSE "GPL-3.0") + include(CPack) include(CheckFunctionExists) @@ -46,6 +53,7 @@ option(ENABLE_SANITIZER_THREAD "Enable TSAN (does not work together with ASAN)." option(ENABLE_MEMORY_PROFILING "Enable dynamic memory tracking." OFF) option(ENABLE_ZLIB "Enable zlib support for nDPId (experimental)." OFF) option(ENABLE_SYSTEMD "Install systemd components." OFF) +option(ENABLE_GNUTLS "Enable GnuTLS support for nDPIsrvd TCP connections." ON) option(BUILD_EXAMPLES "Build C examples." ON) option(BUILD_NDPI "Clone and build nDPI from github." OFF) if(BUILD_NDPI) @@ -134,9 +142,13 @@ if(ENABLE_SANITIZER_THREAD) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize=alignment -fsanitize=enum -fsanitize=thread") endif() if(ENABLE_ZLIB) - set(ZLIB_DEFS "-DENABLE_ZLIB=1") + set(NDPID_DEFS ${NDPID_DEFS} -DENABLE_ZLIB=1) pkg_check_modules(ZLIB REQUIRED zlib) endif() +if(ENABLE_GNUTLS) + set(NDPID_DEFS ${NDPID_DEFS} -DENABLE_GNUTLS=1) + pkg_check_modules(GNUTLS REQUIRED gnutls) +endif() if(NDPI_WITH_GCRYPT) message(STATUS "nDPI: Enable GCRYPT") set(NDPI_ADDITIONAL_ARGS "${NDPI_ADDITIONAL_ARGS} --with-local-libgcrypt") @@ -276,15 +288,17 @@ target_link_libraries(nDPId "${STATIC_LIBNDPI_LIB}" "${pkgcfg_lib_NDPI_ndpi}" target_compile_definitions(nDPIsrvd PRIVATE -D_GNU_SOURCE=1 -DGIT_VERSION=\"${GIT_VERSION}\" ${NDPID_DEFS}) target_include_directories(nDPIsrvd PRIVATE ${NDPID_DEPS_INC}) +target_link_libraries(nDPIsrvd "${pkgcfg_lib_GNUTLS_gnutls}") target_include_directories(nDPId-test PRIVATE ${NDPID_DEPS_INC}) target_compile_options(nDPId-test PRIVATE "-Wno-unused-function" "-pthread") target_compile_definitions(nDPId-test PRIVATE -D_GNU_SOURCE=1 -DNO_MAIN=1 -DGIT_VERSION=\"${GIT_VERSION}\" - ${NDPID_DEFS} ${ZLIB_DEFS} ${NDPID_TEST_MPROF_DEFS}) + ${NDPID_DEFS} ${NDPID_TEST_MPROF_DEFS}) target_include_directories(nDPId-test PRIVATE "${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" ${NDPID_DEPS_INC}) target_link_libraries(nDPId-test "${STATIC_LIBNDPI_LIB}" "${pkgcfg_lib_NDPI_ndpi}" "${pkgcfg_lib_PCRE_pcre}" "${pkgcfg_lib_MAXMINDDB_maxminddb}" "${pkgcfg_lib_ZLIB_z}" + "${pkgcfg_lib_GNUTLS_gnutls}" "${GCRYPT_LIBRARY}" "${GCRYPT_ERROR_LIBRARY}" "${PCAP_LIBRARY}" "${LIBM_LIB}" "-pthread") @@ -364,14 +378,19 @@ install(FILES schema/error_event_schema.json schema/daemon_event_schema.json message(STATUS "--------------------------") message(STATUS "nDPId GIT_VERSION........: ${GIT_VERSION}") message(STATUS "Cross Compilation........: ${CMAKE_CROSSCOMPILING}") +message(STATUS "CMAKE_SYSTEM_NAME........: ${CMAKE_SYSTEM_NAME}") +message(STATUS "CMAKE_SYSTEM_PROCESSOR...: ${CMAKE_SYSTEM_PROCESSOR}") message(STATUS "CMAKE_BUILD_TYPE.........: ${CMAKE_BUILD_TYPE}") message(STATUS "CMAKE_C_FLAGS............: ${CMAKE_C_FLAGS}") -message(STATUS "NDPID_DEFS...............: ${NDPID_DEFS}") +string(REPLACE ";" " " PRETTY_NDPID_DEFS "${NDPID_DEFS}") +message(STATUS "NDPID_DEFS...............: ${PRETTY_NDPID_DEFS}") message(STATUS "ENABLE_COVERAGE..........: ${ENABLE_COVERAGE}") message(STATUS "ENABLE_SANITIZER.........: ${ENABLE_SANITIZER}") message(STATUS "ENABLE_SANITIZER_THREAD..: ${ENABLE_SANITIZER_THREAD}") message(STATUS "ENABLE_MEMORY_PROFILING..: ${ENABLE_MEMORY_PROFILING}") message(STATUS "ENABLE_ZLIB..............: ${ENABLE_ZLIB}") +message(STATUS "ENABLE_SYSTEMD...........: ${ENABLE_SYSTEMD}") +message(STATUS "ENABLE_GNUTLS............: ${ENABLE_GNUTLS}") if(STATIC_LIBNDPI_INSTALLDIR) message(STATUS "STATIC_LIBNDPI_INSTALLDIR: ${STATIC_LIBNDPI_INSTALLDIR}") endif() diff --git a/nDPIsrvd.c b/nDPIsrvd.c index c2478f452..749d3ad6e 100644 --- a/nDPIsrvd.c +++ b/nDPIsrvd.c @@ -1,6 +1,9 @@ #include <arpa/inet.h> #include <errno.h> #include <fcntl.h> +#ifdef ENABLE_GNUTLS +#include <gnutls/gnutls.h> +#endif #include <netdb.h> #include <netinet/tcp.h> #include <pwd.h> @@ -81,6 +84,10 @@ static int distributor_in_sockfd = -1; static struct nDPIsrvd_address distributor_in_address = { .raw.sa_family = 0xFFFF, }; +#ifdef ENABLE_GNUTLS +static gnutls_certificate_credentials_t x509_creds = NULL; +static gnutls_priority_t prio_cache = NULL; +#endif static struct { @@ -93,6 +100,15 @@ static struct nDPIsrvd_ull max_remote_descriptors; nDPIsrvd_ull max_write_buffers; int bufferbloat_fallback_to_blocking; +#ifdef ENABLE_GNUTLS + int enable_tls; + char * tls_proxy_listen_address; + char * tls_proxy_connect_address; + char * x509_certfile; + char * x509_keyfile; + char * x509_crlfile; + char * x509_cafile; +#endif } nDPIsrvd_options = {.pidfile = CMDARG(nDPIsrvd_PIDFILE), .collector_un_sockpath = CMDARG(COLLECTOR_UNIX_SOCKET), .distributor_un_sockpath = CMDARG(DISTRIBUTOR_UNIX_SOCKET), @@ -100,7 +116,6 @@ static struct .user = CMDARG(DEFAULT_CHUSER), .group = CMDARG(NULL), .max_remote_descriptors = nDPIsrvd_MAX_REMOTE_DESCRIPTORS, - .max_write_buffers = nDPIsrvd_MAX_WRITE_BUFFERS, .bufferbloat_fallback_to_blocking = 1}; static void logger_nDPIsrvd(struct remote_desc const * const remote, @@ -169,6 +184,76 @@ void nDPIsrvd_memprof_log(char const * const format, ...) #endif #endif +#ifdef ENABLE_GNUTLS +static int gtls_global_init(char const * const certfile, + char const * const keyfile, + char const * const crlfile, + char const * const cafile, + gnutls_certificate_credentials_t * const x509_creds, + gnutls_priority_t * prio_cache) +{ + int error; + + if (gnutls_check_version("3.4.6") == NULL) + { + logger_early(1, "GnuTLS 3.4.6 or later is required."); + return -1; + } + if ((error = gnutls_global_init()) < 0) + { + logger_early(1, "GnuTLS global init failed %s %s.", gnutls_strerror(error), gnutls_strerror_name(error)); + return -1; + } + if ((error = gnutls_certificate_allocate_credentials(x509_creds)) < 0) + { + logger_early(1, + "GnuTLS certificate allocation failed %s %s.", + gnutls_strerror(error), + gnutls_strerror_name(error)); + return -1; + } + if ((error = gnutls_certificate_set_x509_crl_file(*x509_creds, crlfile, GNUTLS_X509_FMT_PEM)) < 0) + { + logger_early(1, + "GnuTLS x509 crl file invalid. PEM format required %s %s.", + gnutls_strerror(error), + gnutls_strerror_name(error)); + return -1; + } + if ((error = gnutls_certificate_set_x509_key_file(*x509_creds, certfile, keyfile, GNUTLS_X509_FMT_PEM)) < 0) + { + logger_early(1, + "GnuTLS x509 cert/key file invalid. PEM format required %s %s.", + gnutls_strerror(error), + gnutls_strerror_name(error)); + return -1; + } + if ((error = gnutls_certificate_set_x509_trust_file(*x509_creds, cafile, GNUTLS_X509_FMT_PEM)) < 0) + { + logger_early(1, + "GnuTLS x509 ca file invalid. PEM format required: %s %s.", + gnutls_strerror(error), + gnutls_strerror_name(error)); + return -1; + } + if ((error = gnutls_priority_init(prio_cache, "SECURE256", NULL)) < 0) + { + logger_early(1, + "GnuTLS priority cache init failed: %s %s.", + gnutls_strerror(error), + gnutls_strerror_name(error)); + return -1; + } +#if GNUTLS_VERSION_NUMBER >= 0x030506 + gnutls_certificate_set_known_dh_params(*x509_creds, GNUTLS_SEC_PARAM_HIGH); +#else + gnutls_certificate_set_dh_params(*x509_creds, GNUTLS_SEC_PARAM_HIGH); +#endif + + return 0; +} +#endif + static struct nDPIsrvd_json_buffer * get_read_buffer(struct remote_desc * const remote) { switch (remote->sock_type) @@ -791,7 +876,7 @@ static int nDPIsrvd_parse_options(int argc, char ** argv) { int opt; - while ((opt = getopt(argc, argv, "lL:c:dp:s:S:m:u:g:C:Dvh")) != -1) + while ((opt = getopt(argc, argv, "lL:c:dp:s:S:m:u:g:C:DvX:x:h")) != -1) { switch (opt) { @@ -845,6 +930,88 @@ static int nDPIsrvd_parse_options(int argc, char ** argv) case 'v': fprintf(stderr, "%s", get_nDPId_version()); return 1; + case 'X': + { +#ifdef ENABLE_GNUTLS + static char * const x509_subopt_token[] = {"listen", "connect", NULL}; + int errfnd = 0; + char * subopts = optarg; + char * value; + + while (*subopts != '\0' && !errfnd) + { + int subopt = getsubopt(&subopts, x509_subopt_token, &value); + if (subopt == -1) + { + logger_early(1, "Invalid subopt: %s", value); + return 1; + } + + switch (subopt) + { + case 0: /* listen */ + nDPIsrvd_options.tls_proxy_listen_address = strdup(value); + break; + case 1: /* connect */ + nDPIsrvd_options.tls_proxy_connect_address = strdup(value); + break; + default: + logger_early(1, "Invalid subopt: %s", value); + return 1; + } + } + + nDPIsrvd_options.enable_tls = 1; +#else + logger_early(1, "To use TLS capabilities (`-X'), GnuTLS support required."); + return 1; +#endif + break; + } + case 'x': + { +#ifdef ENABLE_GNUTLS + static char * const x509_subopt_token[] = {"cert", "key", "crl", "ca", NULL}; + int errfnd = 0; + char * subopts = optarg; + char * value; + + while (*subopts != '\0' && !errfnd) + { + int subopt = getsubopt(&subopts, x509_subopt_token, &value); + if (subopt == -1) + { + logger_early(1, "Invalid subopt: %s", value); + return 1; + } + + switch (subopt) + { + case 0: /* cert */ + nDPIsrvd_options.x509_certfile = strdup(value); + break; + case 1: /* key */ + nDPIsrvd_options.x509_keyfile = strdup(value); + break; + case 2: /* crl */ + nDPIsrvd_options.x509_crlfile = strdup(value); + break; + case 3: /* ca */ + nDPIsrvd_options.x509_cafile = strdup(value); + break; + default: + logger_early(1, "Invalid subopt: %s", value); + return 1; + } + } + + nDPIsrvd_options.enable_tls = 1; +#else + logger_early(1, "To use TLS capabilities (`-x'), GnuTLS support required."); + return 1; +#endif + break; + } case 'h': default: fprintf(stderr, "%s\n", get_nDPId_version()); @@ -870,6 +1037,10 @@ static int nDPIsrvd_parse_options(int argc, char ** argv) "\t-s\tPath to a listening UNIX socket (nDPIsrvd Distributor).\n" "\t \tDefault: %s\n" "\t-S\tAddress:Port of the listening TCP/IP socket (nDPIsrvd Distributor).\n" +#ifdef ENABLE_GNUTLS + "\t[-X listen=TLS-proxy-listen-address] [-X connect=TLS-proxy-connect-address]\n" + "\t[-x cert=PEM-file] [-x key=PEM-file] [-x crl=file] [-x ca=PEM-file]\n" +#endif "\t-v\tversion\n" "\t-h\tthis\n\n", argv[0], @@ -917,6 +1088,30 @@ static int nDPIsrvd_parse_options(int argc, char ** argv) } } +#ifdef ENABLE_GNUTLS + if (nDPIsrvd_options.enable_tls != 0) + { + if (nDPIsrvd_options.x509_certfile == NULL || nDPIsrvd_options.x509_keyfile == NULL || + nDPIsrvd_options.x509_cafile == NULL) + { + logger_early(1, + "%s: To use nDPIsrvd TLS proxy capabilities, `-x cert', `-x key' and `-x ca' need to be set.", + argv[0]); + return 1; + } + + if ((nDPIsrvd_options.tls_proxy_listen_address == NULL && nDPIsrvd_options.tls_proxy_connect_address == NULL) || + (nDPIsrvd_options.tls_proxy_listen_address != NULL && nDPIsrvd_options.tls_proxy_connect_address != NULL)) + { + logger_early(1, + "%s: To use nDPIsrvd TLS proxy capabilities, either `-X listen' or `-X connect' need to be " + "set.", + argv[0]); + return 1; + } + } +#endif + if (optind < argc) { logger_early(1, "%s: Unexpected argument after options", argv[0]); @@ -1638,6 +1833,18 @@ int main(int argc, char ** argv) goto error_unlink_sockets; } +#ifdef ENABLE_GNUTLS + if (nDPIsrvd_options.enable_tls != 0 && gtls_global_init(nDPIsrvd_options.x509_certfile, + nDPIsrvd_options.x509_keyfile, + nDPIsrvd_options.x509_crlfile, + nDPIsrvd_options.x509_cafile, + &x509_creds, + &prio_cache) != 0) + { + goto error_unlink_sockets; + } +#endif + signal(SIGPIPE, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); diff --git a/scripts/generate-tls-ca.sh b/scripts/generate-tls-ca.sh new file mode 100755 index 000000000..9fe82b677 --- /dev/null +++ b/scripts/generate-tls-ca.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env sh + +set -e + +OUTDIR="$(dirname ${0})" + +printf 'Output directory: %s\n' "${OUTDIR}" + +printf 'ca\ncert_signing_key' > template +certtool --generate-privkey > "${OUTDIR}/ca-key.pem" +certtool --generate-self-signed \ + --template template \ + --load-privkey "${OUTDIR}/ca-key.pem" \ + --outfile "${OUTDIR}/ca-cert.pem" +rm template + +printf 'expiration_days = 365' > template +certtool --generate-crl --load-ca-privkey "${OUTDIR}/ca-key.pem" \ + --template template \ + --load-ca-certificate "${OUTDIR}/ca-cert.pem" \ + --outfile "${OUTDIR}/crl.pem" +rm template + +printf 'encryption_key\nsigning_key' > template +certtool --generate-privkey > "${OUTDIR}/server-key.pem" +certtool --generate-certificate \ + --template template \ + --load-privkey "${OUTDIR}/server-key.pem" \ + --load-ca-certificate "${OUTDIR}/ca-cert.pem" \ + --load-ca-privkey "${OUTDIR}/ca-key.pem" \ + --outfile "${OUTDIR}/server-cert.pem" +rm template diff --git a/scripts/generate-tls-cert.sh b/scripts/generate-tls-cert.sh new file mode 100755 index 000000000..0dccbd054 --- /dev/null +++ b/scripts/generate-tls-cert.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh + +set -e + +OUTDIR="$(dirname ${0})" + +printf 'Output directory: %s\n' "${OUTDIR}" + +printf 'encryption_key\nsigning_key' > template +certtool --generate-privkey > "${OUTDIR}/client-key.pem" +certtool --generate-certificate \ + --template template \ + --load-privkey "${OUTDIR}/client-key.pem" \ + --load-ca-certificate "${OUTDIR}/ca-cert.pem" \ + --load-ca-privkey "${OUTDIR}/ca-key.pem" \ + --outfile "${OUTDIR}/client-cert.pem" +rm template diff --git a/tls-cli.c b/tls-cli.c new file mode 100644 index 000000000..15ad04435 --- /dev/null +++ b/tls-cli.c @@ -0,0 +1,167 @@ +/* This example code is placed in the public domain. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <unistd.h> + +#include <errno.h> + +#define CHECK(x) assert((x) >= 0) + +#define MAX_BUF 1024 +#define MSG "Hello TLS" + +static int tcp_connect(void) +{ + const char * PORT = "5556"; + const char * SERVER = "127.0.0.1"; + int err, sd; + struct sockaddr_in sa; + + /* connects to server + */ + 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, SERVER, &sa.sin_addr); + + err = connect(sd, (struct sockaddr *)&sa, sizeof(sa)); + if (err < 0) + { + fprintf(stderr, "Connect error\n"); + exit(1); + } + + return sd; +} + +static void tcp_close(int sd) +{ + shutdown(sd, SHUT_RDWR); /* no more receptions */ + close(sd); +} + +int main(void) +{ + int ret, sd, ii; + gnutls_session_t session; + char buffer[MAX_BUF + 1], *desc; + gnutls_datum_t out; + int 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)); + + gnutls_certificate_set_x509_key_file(xcred, "client-cert.pem", "client-key.pem", GNUTLS_X509_FMT_PEM); + + CHECK(gnutls_certificate_set_x509_trust_file(xcred, "ca-cert.pem", GNUTLS_X509_FMT_PEM)); + + /* Initialize TLS session */ + CHECK(gnutls_init(&session, GNUTLS_CLIENT)); + + /* 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)); + + sd = tcp_connect(); + + gnutls_transport_set_int(session, sd); + gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + /* Perform the TLS 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 data */ + CHECK(gnutls_record_send(session, MSG, strlen(MSG))); + + 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); + for (ii = 0; ii < ret; ii++) + { + fputc(buffer[ii], stdout); + } + fputs("\n", stdout); + } + + CHECK(gnutls_bye(session, GNUTLS_SHUT_RDWR)); + +end: + + tcp_close(sd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(xcred); + + gnutls_global_deinit(); + + return 0; +} diff --git a/tls-srv.c b/tls-srv.c new file mode 100644 index 000000000..c6636d60a --- /dev/null +++ b/tls-srv.c @@ -0,0 +1,146 @@ +/* This example code is placed in the public domain. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <string.h> +#include <unistd.h> +#include <gnutls/gnutls.h> +#include <assert.h> + +#define CAFILE "ca-cert.pem" +#define KEYFILE "server-key.pem" +#define CERTFILE "server-cert.pem" +#define CRLFILE "crl.pem" + +#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 16 +#define PORT 5556 /* listen to 5556 port */ + +int main(void) +{ + int listen_sd; + int sd, ret; + gnutls_certificate_credentials_t x509_cred; + gnutls_priority_t priority_cache; + struct sockaddr_in sa_serv; + struct sockaddr_in sa_cli; + socklen_t client_len; + char topbuf[512]; + gnutls_session_t session; + char buffer[MAX_BUF + 1]; + int optval = 1; + + /* for backwards compatibility with gnutls < 3.3.0 */ + CHECK(gnutls_global_init()); + + CHECK(gnutls_certificate_allocate_credentials(&x509_cred)); + CHECK(gnutls_certificate_set_x509_crl_file(x509_cred, CRLFILE, GNUTLS_X509_FMT_PEM)); + CHECK(gnutls_certificate_set_x509_key_file(x509_cred, CERTFILE, KEYFILE, GNUTLS_X509_FMT_PEM)); + CHECK(gnutls_certificate_set_x509_trust_file(x509_cred, CAFILE, GNUTLS_X509_FMT_PEM)); + CHECK(gnutls_priority_init(&priority_cache, NULL, NULL)); + +#if GNUTLS_VERSION_NUMBER >= 0x030506 + /* only available since GnuTLS 3.5.6, on previous versions see + * gnutls_certificate_set_dh_params(). */ + gnutls_certificate_set_known_dh_params(x509_cred, GNUTLS_SEC_PARAM_HIGH); +#endif + + /* 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); /* Server Port number */ + + setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int)); + bind(listen_sd, (struct sockaddr *)&sa_serv, sizeof(sa_serv)); + listen(listen_sd, 1024); + + printf("Server ready. Listening to port '%d'.\n", PORT); + + client_len = sizeof(sa_cli); + for (;;) + { + CHECK(gnutls_init(&session, GNUTLS_SERVER)); + CHECK(gnutls_priority_set(session, priority_cache)); + CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred)); + gnutls_session_set_verify_cert(session, NULL, 0); + gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE); + gnutls_certificate_send_x509_rdn_sequence(session, 1); + gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + sd = accept(listen_sd, (struct sockaddr *)&sa_cli, &client_len); + + printf("- connection from %s, port %d\n", + inet_ntop(AF_INET, &sa_cli.sin_addr, topbuf, sizeof(topbuf)), + ntohs(sa_cli.sin_port)); + + gnutls_transport_set_int(session, sd); + + LOOP_CHECK(ret, gnutls_handshake(session)); + if (ret < 0) + { + close(sd); + gnutls_deinit(session); + fprintf(stderr, "*** Handshake has failed (%s)\n", gnutls_strerror(ret)); + continue; + } + printf("- Handshake was completed\n"); + + for (;;) + { + LOOP_CHECK(ret, gnutls_record_recv(session, buffer, MAX_BUF)); + + if (ret == 0) + { + printf("- 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", + ret); + break; + } + else if (ret > 0 && ret < MAX_BUF - 1) + { + buffer[ret] = '$'; + CHECK(gnutls_record_send(session, buffer, ret + 1)); + } + } + LOOP_CHECK(ret, 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; +} |