summaryrefslogtreecommitdiffstats
path: root/src/serv.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:33:12 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:33:12 +0000
commit36082a2fe36ecd800d784ae44c14f1f18c66a7e9 (patch)
tree6c68e0c0097987aff85a01dabddd34b862309a7c /src/serv.c
parentInitial commit. (diff)
downloadgnutls28-36082a2fe36ecd800d784ae44c14f1f18c66a7e9.tar.xz
gnutls28-36082a2fe36ecd800d784ae44c14f1f18c66a7e9.zip
Adding upstream version 3.7.9.upstream/3.7.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/serv.c')
-rw-r--r--src/serv.c2140
1 files changed, 2140 insertions, 0 deletions
diff --git a/src/serv.c b/src/serv.c
new file mode 100644
index 0000000..3ff335d
--- /dev/null
+++ b/src/serv.c
@@ -0,0 +1,2140 @@
+/*
+ * Copyright (C) 2004-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2001,2002 Paul Sheer
+ * Copyright (C) 2016-2018 Red Hat, Inc.
+ * Portions Copyright (C) 2002,2003 Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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, see <https://www.gnu.org/licenses/>.
+ */
+
+/* This server is heavily modified for GnuTLS by Nikos Mavrogiannopoulos
+ * (which means it is quite unreadable)
+ */
+
+#include <config.h>
+
+#include "common.h"
+#include "gnutls-serv-options.h"
+#include "udp-serv.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <socket.h>
+
+/* Gnulib portability files. */
+#include "gl_linked_list.h"
+#include "gl_xlist.h"
+#include "minmax.h"
+#include "read-file.h"
+#include "sockets.h"
+#include "xalloc.h"
+#include "xsize.h"
+
+/* konqueror cannot handle sending the page in multiple
+ * pieces.
+ */
+/* global stuff */
+static int generate = 0;
+static int http = 0;
+static int strip_crlf = 1;
+static int x509ctype;
+static int debug = 0;
+
+unsigned int verbose = 1;
+static int nodb;
+static int noticket;
+static int earlydata;
+int require_cert;
+int disable_client_cert;
+
+const char *psk_passwd = NULL;
+const char *srp_passwd = NULL;
+const char *srp_passwd_conf = NULL;
+const char **x509_keyfile = NULL;
+const char **x509_certfile = NULL;
+unsigned x509_certfile_size = 0;
+unsigned x509_keyfile_size = 0;
+const char *x509_cafile = NULL;
+const char *dh_params_file = NULL;
+const char *x509_crlfile = NULL;
+const char *priorities = NULL;
+const char **rawpk_keyfile = NULL;
+const char **rawpk_file = NULL;
+unsigned rawpk_keyfile_size = 0;
+unsigned rawpk_file_size = 0;
+
+const char **ocsp_responses = NULL;
+unsigned ocsp_responses_size = 0;
+
+const char *sni_hostname = NULL;
+int sni_hostname_fatal = 0;
+
+const char **alpn_protos = NULL;
+unsigned alpn_protos_size = 0;
+
+gnutls_datum_t session_ticket_key;
+gnutls_anti_replay_t anti_replay;
+int record_max_size;
+const char *http_data_file = NULL;
+static void tcp_server(const char *name, int port);
+
+/* end of globals */
+
+/* This is a sample TCP echo server.
+ * This will behave as an http server if any argument in the
+ * command line is present
+ */
+
+#define SMALL_READ_TEST (2147483647)
+
+#define GERR(ret) fprintf(stderr, "Error: %s\n", safe_strerror(ret))
+
+#define HTTP_END "</BODY></HTML>\n\n"
+
+#define HTTP_UNIMPLEMENTED "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<HTML><HEAD>\r\n<TITLE>501 Method Not Implemented</TITLE>\r\n</HEAD><BODY>\r\n<H1>Method Not Implemented</H1>\r\n<HR>\r\n</BODY></HTML>\r\n"
+#define HTTP_OK "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n"
+
+#define HTTP_BEGIN HTTP_OK \
+ "\n" \
+ "<HTML><BODY>\n" \
+ "<CENTER><H1>This is <a href=\"http://www.gnu.org/software/gnutls\">" \
+ "GnuTLS</a></H1></CENTER>\n\n"
+
+/* These are global */
+gnutls_srp_server_credentials_t srp_cred = NULL;
+gnutls_psk_server_credentials_t psk_cred = NULL;
+#ifdef ENABLE_ANON
+gnutls_anon_server_credentials_t dh_cred = NULL;
+#endif
+gnutls_certificate_credentials_t cert_cred = NULL;
+
+const int ssl_session_cache = 2048;
+
+static void wrap_db_init(void);
+static void wrap_db_deinit(void);
+static int wrap_db_store(void *dbf, gnutls_datum_t key,
+ gnutls_datum_t data);
+static gnutls_datum_t wrap_db_fetch(void *dbf, gnutls_datum_t key);
+static int wrap_db_delete(void *dbf, gnutls_datum_t key);
+static int anti_replay_db_add(void *dbf, time_t exp, const gnutls_datum_t *key,
+ const gnutls_datum_t *data);
+
+static void cmd_parser(int argc, char **argv);
+
+
+#define HTTP_STATE_REQUEST 1
+#define HTTP_STATE_RESPONSE 2
+#define HTTP_STATE_CLOSING 3
+
+typedef struct {
+ char *http_request;
+ char *http_response;
+ int request_length;
+ int response_length;
+ int response_written;
+ int http_state;
+ int listen_socket;
+ int fd;
+ gnutls_session_t tls_session;
+ int handshake_ok;
+ int close_ok;
+ time_t start;
+ int earlydata_eof;
+} listener_item;
+
+static const char *safe_strerror(int value)
+{
+ const char *ret = gnutls_strerror(value);
+ if (ret == NULL)
+ ret = str_unknown;
+ return ret;
+}
+
+static void listener_free(const void *elt)
+{
+ listener_item *j = (listener_item *)elt;
+
+ free(j->http_request);
+ free(j->http_response);
+ if (j->fd >= 0) {
+ if (j->close_ok)
+ gnutls_bye(j->tls_session, GNUTLS_SHUT_WR);
+ shutdown(j->fd, 2);
+ close(j->fd);
+ gnutls_deinit(j->tls_session);
+ }
+}
+
+
+/* we use primes up to 1024 in this server.
+ * otherwise we should add them here.
+ */
+
+gnutls_dh_params_t dh_params = NULL;
+gnutls_rsa_params_t rsa_params = NULL;
+
+static int generate_dh_primes(void)
+{
+ int prime_bits =
+ gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
+ GNUTLS_SEC_PARAM_MEDIUM);
+
+ if (gnutls_dh_params_init(&dh_params) < 0) {
+ fprintf(stderr, "Error in dh parameter initialization\n");
+ exit(1);
+ }
+
+ /* Generate Diffie-Hellman parameters - for use with DHE
+ * kx algorithms. These should be discarded and regenerated
+ * once a week or once a month. Depends on the
+ * security requirements.
+ */
+ printf
+ ("Generating Diffie-Hellman parameters [%d]. Please wait...\n",
+ prime_bits);
+ fflush(stdout);
+
+ if (gnutls_dh_params_generate2(dh_params, prime_bits) < 0) {
+ fprintf(stderr, "Error in prime generation\n");
+ exit(1);
+ }
+
+ return 0;
+}
+
+static void read_dh_params(void)
+{
+ char tmpdata[2048];
+ int size;
+ gnutls_datum_t params;
+ FILE *fp;
+
+ if (gnutls_dh_params_init(&dh_params) < 0) {
+ fprintf(stderr, "Error in dh parameter initialization\n");
+ exit(1);
+ }
+
+ /* read the params file
+ */
+ fp = fopen(dh_params_file, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Could not open %s\n", dh_params_file);
+ exit(1);
+ }
+
+ size = fread(tmpdata, 1, sizeof(tmpdata) - 1, fp);
+ tmpdata[size] = 0;
+ fclose(fp);
+
+ params.data = (unsigned char *) tmpdata;
+ params.size = size;
+
+ size =
+ gnutls_dh_params_import_pkcs3(dh_params, &params,
+ GNUTLS_X509_FMT_PEM);
+
+ if (size < 0) {
+ fprintf(stderr, "Error parsing dh params: %s\n",
+ safe_strerror(size));
+ exit(1);
+ }
+
+ printf("Read Diffie-Hellman parameters.\n");
+ fflush(stdout);
+
+}
+
+static int
+get_params(gnutls_session_t session, gnutls_params_type_t type,
+ gnutls_params_st * st)
+{
+
+ if (type == GNUTLS_PARAMS_DH) {
+ if (dh_params == NULL)
+ return -1;
+ st->params.dh = dh_params;
+ } else
+ return -1;
+
+ st->type = type;
+ st->deinit = 0;
+
+ return 0;
+}
+
+static gl_list_t listener_list;
+
+static int cert_verify_callback(gnutls_session_t session)
+{
+listener_item * j = gnutls_session_get_ptr(session);
+unsigned int size;
+int ret;
+
+ if (gnutls_auth_get_type(session) == GNUTLS_CRD_CERTIFICATE) {
+ if (!require_cert && gnutls_certificate_get_peers(session, &size) == NULL)
+ return 0;
+
+ if (ENABLED_OPT(VERIFY_CLIENT_CERT)) {
+ if (cert_verify(session, NULL, NULL) == 0) {
+ do {
+ ret = gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_ACCESS_DENIED);
+ } while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
+
+ j->http_state = HTTP_STATE_CLOSING;
+ return -1;
+ }
+ } else {
+ printf("- Peer's certificate was NOT verified.\n");
+ }
+ }
+ return 0;
+}
+
+/* callback used to verify if the host name advertised in client hello matches
+ * the one configured in server
+ */
+static int
+post_client_hello(gnutls_session_t session)
+{
+ int ret;
+ /* DNS names (only type supported) may be at most 256 byte long */
+ char *name;
+ size_t len = 256;
+ unsigned int type;
+ int i;
+
+ name = malloc(len);
+ if (name == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ for (i=0; ; ) {
+ ret = gnutls_server_name_get(session, name, &len, &type, i);
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ char *new_name;
+ new_name = realloc(name, len);
+ if (new_name == NULL) {
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto end;
+ }
+ name = new_name;
+ continue; /* retry call with same index */
+ }
+
+ /* check if it is the last entry in list */
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ i++;
+ if (ret != GNUTLS_E_SUCCESS)
+ goto end;
+ /* unknown types need to be ignored */
+ if (type != GNUTLS_NAME_DNS)
+ continue;
+
+ if (strlen(sni_hostname) != len)
+ continue;
+ /* API guarantees that the name of type DNS will be null terminated */
+ if (!strncmp(name, sni_hostname, len)) {
+ ret = GNUTLS_E_SUCCESS;
+ goto end;
+ }
+ };
+ /* when there is no extension, we can't send the extension specific alert */
+ if (i == 0) {
+ fprintf(stderr, "Warning: client did not include SNI extension, using default host\n");
+ ret = GNUTLS_E_SUCCESS;
+ goto end;
+ }
+
+ if (sni_hostname_fatal == 1) {
+ /* abort the connection, propagate error up the stack */
+ ret = GNUTLS_E_UNRECOGNIZED_NAME;
+ goto end;
+ }
+
+ fprintf(stderr, "Warning: client provided unrecognized host name\n");
+ /* since we just want to send an alert, not abort the connection, we
+ * need to send it ourselves
+ */
+ do {
+ ret = gnutls_alert_send(session,
+ GNUTLS_AL_WARNING,
+ GNUTLS_A_UNRECOGNIZED_NAME);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ /* continue handshake, fall through */
+end:
+ free(name);
+ return ret;
+}
+
+#define MAX_ALPN_PROTOCOLS 16
+gnutls_session_t initialize_session(int dtls)
+{
+ gnutls_session_t session;
+ int ret;
+ unsigned i;
+ const char *err;
+#ifdef ENABLE_ALPN
+ gnutls_datum_t alpn[MAX_ALPN_PROTOCOLS];
+#endif
+ unsigned alpn_size;
+ unsigned flags = GNUTLS_SERVER | GNUTLS_POST_HANDSHAKE_AUTH | GNUTLS_ENABLE_RAWPK;
+
+ if (dtls)
+ flags |= GNUTLS_DATAGRAM;
+
+ if (earlydata)
+ flags |= GNUTLS_ENABLE_EARLY_DATA;
+
+ gnutls_init(&session, flags);
+
+ /* allow the use of private ciphersuites.
+ */
+ gnutls_handshake_set_private_extensions(session, 1);
+
+ gnutls_handshake_set_timeout(session,
+ GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+
+ if (nodb == 0) {
+ gnutls_db_set_retrieve_function(session, wrap_db_fetch);
+ gnutls_db_set_remove_function(session, wrap_db_delete);
+ gnutls_db_set_store_function(session, wrap_db_store);
+ gnutls_db_set_ptr(session, NULL);
+ }
+
+ if (noticket == 0)
+ gnutls_session_ticket_enable_server(session,
+ &session_ticket_key);
+
+ if (earlydata) {
+ gnutls_anti_replay_enable(session, anti_replay);
+ if (HAVE_OPT(MAXEARLYDATA)) {
+ ret = gnutls_record_set_max_early_data_size(session, OPT_VALUE_MAXEARLYDATA);
+ if (ret < 0) {
+ fprintf(stderr, "Could not set max early data size: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ }
+ }
+
+ if (sni_hostname != NULL)
+ gnutls_handshake_set_post_client_hello_function(session,
+ &post_client_hello);
+
+ if (priorities == NULL) {
+ ret = gnutls_set_default_priority(session);
+ if (ret < 0) {
+ fprintf(stderr, "Could not set default policy: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ } else {
+ ret = gnutls_priority_set_direct(session, priorities, &err);
+ if (ret < 0) {
+ fprintf(stderr, "Syntax error at: %s\n", err);
+ exit(1);
+ }
+ }
+
+#ifndef ENABLE_ALPN
+ if (alpn_protos_size != 0) {
+ fprintf(stderr, "ALPN is not supported\n");
+ exit(1);
+ }
+#else
+ alpn_size = MIN(MAX_ALPN_PROTOCOLS,alpn_protos_size);
+ for (i=0;i<alpn_size;i++) {
+ alpn[i].data = (void*)alpn_protos[i];
+ alpn[i].size = strlen(alpn_protos[i]);
+ }
+
+ ret = gnutls_alpn_set_protocols(session, alpn, alpn_size, HAVE_OPT(ALPN_FATAL)?GNUTLS_ALPN_MANDATORY:0);
+ if (ret < 0) {
+ fprintf(stderr, "Error setting ALPN protocols: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+#endif
+
+#ifdef ENABLE_ANON
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, dh_cred);
+#endif
+
+ if (srp_cred != NULL)
+ gnutls_credentials_set(session, GNUTLS_CRD_SRP, srp_cred);
+
+ if (psk_cred != NULL)
+ gnutls_credentials_set(session, GNUTLS_CRD_PSK, psk_cred);
+
+ if (cert_cred != NULL) {
+ gnutls_certificate_set_verify_function(cert_cred,
+ cert_verify_callback);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ cert_cred);
+ }
+
+ if (disable_client_cert)
+ gnutls_certificate_server_set_request(session,
+ GNUTLS_CERT_IGNORE);
+ else {
+ if (require_cert)
+ gnutls_certificate_server_set_request(session,
+ GNUTLS_CERT_REQUIRE);
+ else
+ gnutls_certificate_server_set_request(session,
+ GNUTLS_CERT_REQUEST);
+ }
+
+ /* use the record size limit extension */
+ if (record_max_size > 0) {
+ if (gnutls_record_set_max_recv_size(session, record_max_size) <
+ 0) {
+ fprintf(stderr,
+ "Cannot set the maximum record receive size to %d.\n",
+ record_max_size);
+ exit(1);
+ }
+ }
+
+ if (HAVE_OPT(COMPRESS_CERT)) {
+ ret = compress_cert_set_methods(session,
+ OPTS_ARRAY(COMPRESS_CERT),
+ OPTS_COUNT(COMPRESS_CERT));
+ if (ret < 0)
+ exit(1);
+ }
+
+ if (HAVE_OPT(HEARTBEAT))
+ gnutls_heartbeat_enable(session,
+ GNUTLS_HB_PEER_ALLOWED_TO_SEND);
+
+#ifdef ENABLE_DTLS_SRTP
+ if (HAVE_OPT(SRTP_PROFILES)) {
+ ret =
+ gnutls_srtp_set_profile_direct(session,
+ OPT_ARG(SRTP_PROFILES),
+ &err);
+ if (ret == GNUTLS_E_INVALID_REQUEST)
+ fprintf(stderr, "Syntax error at: %s\n", err);
+ else if (ret != 0)
+ fprintf(stderr, "Error in profiles: %s\n",
+ gnutls_strerror(ret));
+ else fprintf(stderr,"DTLS profile set to %s\n",
+ OPT_ARG(SRTP_PROFILES));
+
+ if (ret != 0) exit(1);
+ }
+#endif
+
+
+ return session;
+}
+
+#include <gnutls/x509.h>
+
+static const char DEFAULT_DATA[] =
+ "This is the default message reported by the GnuTLS implementation. "
+ "For more information please visit "
+ "<a href=\"https://www.gnutls.org/\">https://www.gnutls.org/</a>.";
+
+/* Creates html with the current session information.
+ */
+#define tmp_buffer &http_buffer[strlen(http_buffer)]
+#define tmp_buffer_size len-strlen(http_buffer)
+static char *peer_print_info(gnutls_session_t session, int *ret_length,
+ const char *header)
+{
+ const char *tmp;
+ unsigned char sesid[32];
+ size_t i, sesid_size;
+ char *http_buffer, *desc;
+ gnutls_kx_algorithm_t kx_alg;
+ size_t len = 20 * 1024 + strlen(header);
+ char *crtinfo = NULL;
+ gnutls_protocol_t version;
+ size_t ncrtinfo = 0;
+
+ if (verbose == 0) {
+ http_buffer = malloc(len);
+ if (http_buffer == NULL)
+ return NULL;
+
+ strcpy(http_buffer, HTTP_BEGIN);
+ strcpy(&http_buffer[sizeof(HTTP_BEGIN) - 1], DEFAULT_DATA);
+ strcpy(&http_buffer
+ [sizeof(HTTP_BEGIN) + sizeof(DEFAULT_DATA) - 2],
+ HTTP_END);
+ *ret_length =
+ sizeof(DEFAULT_DATA) + sizeof(HTTP_BEGIN) +
+ sizeof(HTTP_END) - 3;
+ return http_buffer;
+ }
+
+ if (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT) == GNUTLS_CRT_X509) {
+ const gnutls_datum_t *cert_list;
+ unsigned int cert_list_size = 0;
+
+ cert_list =
+ gnutls_certificate_get_peers(session, &cert_list_size);
+
+ for (i = 0; i < cert_list_size; i++) {
+ gnutls_x509_crt_t cert = NULL;
+ gnutls_datum_t info;
+
+ if (gnutls_x509_crt_init(&cert) == 0 &&
+ gnutls_x509_crt_import(cert, &cert_list[i],
+ GNUTLS_X509_FMT_DER) ==
+ 0
+ && gnutls_x509_crt_print(cert,
+ GNUTLS_CRT_PRINT_FULL,
+ &info) == 0) {
+ const char post[] = "</PRE><P><PRE>";
+ char *crtinfo_new;
+ size_t ncrtinfo_new;
+
+ ncrtinfo_new = xsum3(ncrtinfo, info.size,
+ sizeof(post));
+ if (size_overflow_p(ncrtinfo_new)) {
+ free(crtinfo);
+ return NULL;
+ }
+ crtinfo_new = realloc(crtinfo, ncrtinfo_new);
+ if (crtinfo_new == NULL) {
+ free(crtinfo);
+ return NULL;
+ }
+ crtinfo = crtinfo_new;
+ memcpy(crtinfo + ncrtinfo, info.data,
+ info.size);
+ ncrtinfo += info.size;
+ memcpy(crtinfo + ncrtinfo, post,
+ strlen(post));
+ ncrtinfo += strlen(post);
+ crtinfo[ncrtinfo] = '\0';
+ gnutls_free(info.data);
+ }
+ gnutls_x509_crt_deinit(cert);
+ }
+ }
+
+ http_buffer = malloc(len);
+ if (http_buffer == NULL) {
+ free(crtinfo);
+ return NULL;
+ }
+
+ strcpy(http_buffer, HTTP_BEGIN);
+
+ version = gnutls_protocol_get_version(session);
+
+ /* print session_id */
+ sesid_size = sizeof(sesid);
+ gnutls_session_get_id(session, sesid, &sesid_size);
+ snprintf(tmp_buffer, tmp_buffer_size, "\n<p>Session ID: <i>");
+ for (i = 0; i < sesid_size; i++)
+ snprintf(tmp_buffer, tmp_buffer_size, "%.2X", sesid[i]);
+ snprintf(tmp_buffer, tmp_buffer_size, "</i></p>\n");
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<h5>If your browser supports session resumption, then you should see the "
+ "same session ID, when you press the <b>reload</b> button.</h5>\n");
+
+ /* Here unlike print_info() we use the kx algorithm to distinguish
+ * the functions to call.
+ */
+ {
+ char dns[256];
+ size_t dns_size = sizeof(dns);
+ unsigned int type;
+
+ if (gnutls_server_name_get
+ (session, dns, &dns_size, &type, 0) == 0) {
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "\n<p>Server Name: %s</p>\n", dns);
+ }
+
+ }
+
+ kx_alg = gnutls_kx_get(session);
+
+ /* print srp specific data */
+#ifdef ENABLE_SRP
+ if (kx_alg == GNUTLS_KX_SRP) {
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<p>Connected as user '%s'.</p>\n",
+ gnutls_srp_server_get_username(session));
+ }
+#endif
+
+#ifdef ENABLE_PSK
+ if (kx_alg == GNUTLS_KX_PSK && gnutls_psk_server_get_username(session)) {
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<p>Connected as user '%s'.</p>\n",
+ gnutls_psk_server_get_username(session));
+ }
+#endif
+
+
+ /* print session information */
+ strcat(http_buffer, "<P>\n");
+
+ tmp =
+ gnutls_protocol_get_name(version);
+ if (tmp == NULL)
+ tmp = str_unknown;
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<TABLE border=1><TR><TD>Protocol version:</TD><TD>%s</TD></TR>\n",
+ tmp);
+
+ desc = gnutls_session_get_desc(session);
+ if (desc) {
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<TR><TD>Description:</TD><TD>%s</TD></TR>\n",
+ desc);
+ gnutls_free(desc);
+ }
+
+ if (gnutls_auth_get_type(session) == GNUTLS_CRD_CERTIFICATE &&
+ gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT) != GNUTLS_CRT_X509) {
+ tmp =
+ gnutls_certificate_type_get_name
+ (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT));
+ if (tmp == NULL)
+ tmp = str_unknown;
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<TR><TD>Certificate Type:</TD><TD>%s</TD></TR>\n",
+ tmp);
+ }
+
+ if (version < GNUTLS_TLS1_3) {
+ tmp = gnutls_kx_get_name(kx_alg);
+ if (tmp == NULL)
+ tmp = str_unknown;
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<TR><TD>Key Exchange:</TD><TD>%s</TD></TR>\n", tmp);
+
+#ifdef ENABLE_ANON
+ if (kx_alg == GNUTLS_KX_ANON_DH) {
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<p> Connect using anonymous DH (prime of %d bits)</p>\n",
+ gnutls_dh_get_prime_bits(session));
+ }
+#endif
+
+#if defined(ENABLE_DHE) || defined(ENABLE_ANON)
+ if (kx_alg == GNUTLS_KX_DHE_RSA || kx_alg == GNUTLS_KX_DHE_DSS) {
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "Ephemeral DH using prime of <b>%d</b> bits.<br>\n",
+ gnutls_dh_get_prime_bits(session));
+ }
+#endif
+
+ tmp = gnutls_compression_get_name(gnutls_compression_get(session));
+ if (tmp == NULL)
+ tmp = str_unknown;
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<TR><TD>Compression</TD><TD>%s</TD></TR>\n", tmp);
+
+ tmp = gnutls_cipher_suite_get_name(kx_alg,
+ gnutls_cipher_get(session),
+ gnutls_mac_get(session));
+ if (tmp == NULL)
+ tmp = str_unknown;
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<TR><TD>Ciphersuite</TD><TD>%s</TD></TR>\n",
+ tmp);
+ }
+
+ tmp = gnutls_cipher_get_name(gnutls_cipher_get(session));
+ if (tmp == NULL)
+ tmp = str_unknown;
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<TR><TD>Cipher</TD><TD>%s</TD></TR>\n", tmp);
+
+ tmp = gnutls_mac_get_name(gnutls_mac_get(session));
+ if (tmp == NULL)
+ tmp = str_unknown;
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<TR><TD>MAC</TD><TD>%s</TD></TR>\n", tmp);
+
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "</TABLE></P>\n");
+
+ if (crtinfo) {
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<hr><PRE>%s\n</PRE>\n", crtinfo);
+ free(crtinfo);
+ }
+
+ snprintf(tmp_buffer, tmp_buffer_size,
+ "<hr><P>Your HTTP header was:<PRE>%s</PRE></P>\n"
+ HTTP_END, header);
+
+ *ret_length = strlen(http_buffer);
+
+ return http_buffer;
+}
+
+static char *peer_print_data(gnutls_session_t session, int *ret_length)
+{
+ gnutls_datum_t data;
+ char *http_buffer;
+ size_t len;
+ int ret;
+
+ ret = gnutls_load_file(http_data_file, &data);
+ if (ret < 0) {
+ ret = asprintf(&http_buffer,
+ "HTTP/1.0 404 Not Found\r\n"
+ "Content-type: text/html\r\n"
+ "\r\n"
+ "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
+ "<BODY><H1>Couldn't read %s</H1></BODY></HTML>\n\n",
+ http_data_file);
+ if (ret < 0)
+ return NULL;
+
+ *ret_length = strlen(http_buffer);
+ return http_buffer;
+ }
+
+ ret = asprintf(&http_buffer,
+ "HTTP/1.0 200 OK\r\n"
+ "Content-Type: application/octet-stream\r\n"
+ "Content-Length: %u\r\n"
+ "\r\n",
+ data.size);
+ if (ret < 0)
+ return NULL;
+ len = ret;
+ http_buffer = realloc(http_buffer, len + data.size);
+ memcpy(&http_buffer[len], data.data, data.size);
+ gnutls_free(data.data);
+ *ret_length = len + data.size;
+ return http_buffer;
+}
+
+const char *human_addr(const struct sockaddr *sa, socklen_t salen,
+ char *buf, size_t buflen)
+{
+ const char *save_buf = buf;
+ size_t l;
+
+ if (!buf || !buflen)
+ return "(error)";
+
+ *buf = 0;
+
+ switch (sa->sa_family) {
+#if HAVE_IPV6
+ case AF_INET6:
+ snprintf(buf, buflen, "IPv6 ");
+ break;
+#endif
+
+ case AF_INET:
+ snprintf(buf, buflen, "IPv4 ");
+ break;
+ }
+
+ l = 5;
+ buf += l;
+ buflen -= l;
+
+ if (getnameinfo(sa, salen, buf, buflen, NULL, 0, NI_NUMERICHOST) !=
+ 0) {
+ return "(error)";
+ }
+
+ l = strlen(buf);
+ buf += l;
+ buflen -= l;
+
+ if (buflen < 8)
+ return save_buf;
+
+ strcat(buf, " port ");
+ buf += 6;
+ buflen -= 6;
+
+ if (getnameinfo(sa, salen, NULL, 0, buf, buflen, NI_NUMERICSERV) !=
+ 0) {
+ snprintf(buf, buflen, "%s", " unknown");
+ }
+
+ return save_buf;
+}
+
+int wait_for_connection(void)
+{
+ fd_set rd, wr;
+ int n, sock = -1;
+ gl_list_iterator_t iter;
+ const void *elt;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ n = 0;
+
+ iter = gl_list_iterator(listener_list);
+ while (gl_list_iterator_next(&iter, &elt, NULL)) {
+ const listener_item *j = elt;
+
+ if (j->listen_socket) {
+ FD_SET(j->fd, &rd);
+ n = MAX(n, j->fd);
+ }
+ }
+ gl_list_iterator_free(&iter);
+
+ /* waiting part */
+ n = select(n + 1, &rd, &wr, NULL, NULL);
+ if (n == -1 && errno == EINTR)
+ return -1;
+ if (n < 0) {
+ perror("select()");
+ exit(1);
+ }
+
+ /* find which one is ready */
+ iter = gl_list_iterator(listener_list);
+ while (gl_list_iterator_next(&iter, &elt, NULL)) {
+ const listener_item *j = elt;
+
+ /* a new connection has arrived */
+ if (FD_ISSET(j->fd, &rd) && j->listen_socket) {
+ sock = j->fd;
+ break;
+ }
+ }
+ gl_list_iterator_free(&iter);
+ return sock;
+}
+
+int listen_socket(const char *name, int listen_port, int socktype)
+{
+ struct addrinfo hints, *res, *ptr;
+ char portname[6];
+ int s = -1;
+ int yes;
+ listener_item *j = NULL;
+
+ snprintf(portname, sizeof(portname), "%d", listen_port);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = socktype;
+ hints.ai_flags = AI_PASSIVE;
+
+ if ((s = getaddrinfo(NULL, portname, &hints, &res)) != 0) {
+ fprintf(stderr, "getaddrinfo() failed: %s\n",
+ gai_strerror(s));
+ return -1;
+ }
+
+ for (ptr = res; ptr != NULL; ptr = ptr->ai_next) {
+ int news;
+#ifndef HAVE_IPV6
+ if (ptr->ai_family != AF_INET)
+ continue;
+#endif
+
+ /* Print what we are doing. */
+ {
+ char topbuf[512];
+
+ fprintf(stderr, "%s listening on %s...",
+ name, human_addr(ptr->ai_addr,
+ ptr->ai_addrlen, topbuf,
+ sizeof(topbuf)));
+ }
+
+ if ((news = socket(ptr->ai_family, ptr->ai_socktype,
+ ptr->ai_protocol)) < 0) {
+ perror("socket() failed");
+ continue;
+ }
+ s = news; /* to not overwrite existing s from previous loops */
+#if defined(HAVE_IPV6) && !defined(_WIN32)
+ if (ptr->ai_family == AF_INET6) {
+ yes = 1;
+ /* avoid listen on ipv6 addresses failing
+ * because already listening on ipv4 addresses: */
+ (void)setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ (const void *) &yes, sizeof(yes));
+ }
+#endif
+
+ if (socktype == SOCK_STREAM) {
+ yes = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (const void *) &yes,
+ sizeof(yes)) < 0) {
+ perror("setsockopt() failed");
+ close(s);
+ continue;
+ }
+ } else {
+#if defined(IP_DONTFRAG)
+ yes = 1;
+ if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
+ (const void *) &yes,
+ sizeof(yes)) < 0)
+ perror("setsockopt(IP_DF) failed");
+#elif defined(IP_MTU_DISCOVER)
+ yes = IP_PMTUDISC_DO;
+ if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
+ (const void *) &yes,
+ sizeof(yes)) < 0)
+ perror("setsockopt(IP_DF) failed");
+#endif
+ }
+
+ if (bind(s, ptr->ai_addr, ptr->ai_addrlen) < 0) {
+ perror("bind() failed");
+ close(s);
+ continue;
+ }
+
+ if (socktype == SOCK_STREAM) {
+ if (listen(s, 10) < 0) {
+ perror("listen() failed");
+ exit(1);
+ }
+ }
+
+ /* new list entry for the connection */
+ j = xzalloc(sizeof(*j));
+ gl_list_add_last(listener_list, j);
+ j->listen_socket = 1;
+ j->fd = s;
+
+ /* Complete earlier message. */
+ fprintf(stderr, "done\n");
+ }
+
+ fflush(stderr);
+
+ freeaddrinfo(res);
+
+ return s;
+}
+
+/* strips \r\n from the end of the string
+ */
+static void strip(char *data)
+{
+ int i;
+ int len = strlen(data);
+
+ for (i = 0; i < len; i++) {
+ if (data[i] == '\r' && data[i + 1] == '\n'
+ && data[i + 2] == 0) {
+ data[i] = '\n';
+ data[i + 1] = 0;
+ break;
+ }
+ }
+}
+
+static unsigned
+get_response(gnutls_session_t session, char *request,
+ char **response, int *response_length)
+{
+ char *p, *h;
+
+ if (http != 0) {
+ if (strncmp(request, "GET ", 4))
+ goto unimplemented;
+
+ if (!(h = strchr(request, '\n')))
+ goto unimplemented;
+
+ *h++ = '\0';
+ while (*h == '\r' || *h == '\n')
+ h++;
+
+ if (!(p = strchr(request + 4, ' ')))
+ goto unimplemented;
+ *p = '\0';
+ }
+
+ if (http != 0) {
+ if (http_data_file == NULL)
+ *response = peer_print_info(session, response_length, h);
+ else
+ *response = peer_print_data(session, response_length);
+ } else {
+ int ret;
+ if (strip_crlf != 0)
+ strip(request);
+ fprintf(stderr, "received cmd: %s\n", request);
+
+ ret = check_command(session, request, disable_client_cert);
+ if (ret > 0) {
+ *response = strdup("Successfully executed command\n");
+ if (*response == NULL) {
+ fprintf(stderr, "Memory error\n");
+ return 0;
+ }
+ *response_length = strlen(*response);
+ return 1;
+ } else if (ret == 0) {
+ *response = strdup(request);
+ if (*response == NULL) {
+ fprintf(stderr, "Memory error\n");
+ return 0;
+ }
+ *response_length = strlen(*response);
+ } else {
+ *response = NULL;
+ do {
+ ret = gnutls_alert_send_appropriate(session, ret);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ return 0;
+ }
+ }
+
+ return 1;
+
+ unimplemented:
+ *response = strdup(HTTP_UNIMPLEMENTED);
+ if (*response == NULL)
+ return 0;
+ *response_length = ((*response) ? strlen(*response) : 0);
+ return 1;
+}
+
+static void terminate(int sig) __attribute__ ((__noreturn__));
+
+static void terminate(int sig)
+{
+ char buf[64] = { 0 };
+ char *p;
+
+ /* This code must be async-signal-safe. */
+ p = stpcpy(buf, "Exiting via signal ");
+
+ if (sig > 10)
+ *p++ = '0' + sig / 10;
+ *p++ = '0' + sig % 10;
+ *p++ = '\n';
+
+ write(STDERR_FILENO, buf, p - buf);
+ _exit(1);
+}
+
+
+static void check_alert(gnutls_session_t session, int ret)
+{
+ if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
+ || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+ int last_alert = gnutls_alert_get(session);
+ if (last_alert == GNUTLS_A_NO_RENEGOTIATION &&
+ ret == GNUTLS_E_WARNING_ALERT_RECEIVED)
+ printf
+ ("* Received NO_RENEGOTIATION alert. Client does not support renegotiation.\n");
+ else
+ printf("* Received alert '%d': %s.\n", last_alert,
+ gnutls_alert_get_name(last_alert));
+ }
+}
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+static void tls_audit_log_func(gnutls_session_t session, const char *str)
+{
+ fprintf(stderr, "|<%p>| %s", session, str);
+}
+
+int main(int argc, char **argv)
+{
+ int ret, mtu, port;
+ char name[256];
+ int cert_set = 0;
+ unsigned use_static_dh_params = 0;
+ unsigned i;
+
+ cmd_parser(argc, argv);
+
+#ifndef _WIN32
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGTERM, terminate);
+ if (signal(SIGINT, terminate) == SIG_IGN)
+ signal(SIGINT, SIG_IGN); /* e.g. background process */
+#endif
+
+ sockets_init();
+
+ listener_list = gl_list_create_empty(GL_LINKED_LIST,
+ NULL, NULL, listener_free,
+ true);
+
+ if (nodb == 0)
+ wrap_db_init();
+
+ if (HAVE_OPT(UDP))
+ strcpy(name, "UDP ");
+ else
+ name[0] = 0;
+
+ if (http == 1) {
+ strcat(name, "HTTP Server");
+ } else {
+ strcat(name, "Echo Server");
+ }
+
+ gnutls_global_set_log_function(tls_log_func);
+ gnutls_global_set_audit_log_function(tls_audit_log_func);
+ gnutls_global_set_log_level(debug);
+
+ if ((ret = gnutls_global_init()) < 0) {
+ fprintf(stderr, "global_init: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+#ifdef ENABLE_PKCS11
+ if (HAVE_OPT(PROVIDER)) {
+ ret = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL);
+ if (ret < 0)
+ fprintf(stderr, "pkcs11_init: %s",
+ gnutls_strerror(ret));
+ else {
+ ret =
+ gnutls_pkcs11_add_provider(OPT_ARG(PROVIDER),
+ NULL);
+ if (ret < 0) {
+ fprintf(stderr, "pkcs11_add_provider: %s",
+ gnutls_strerror(ret));
+ exit(1);
+ }
+ }
+ }
+ pkcs11_common(NULL);
+#endif
+
+ /* Note that servers must generate parameters for
+ * Diffie-Hellman. See gnutls_dh_params_generate(), and
+ * gnutls_dh_params_set().
+ */
+ if (generate != 0) {
+ generate_dh_primes();
+ } else if (dh_params_file) {
+ read_dh_params();
+ } else {
+ use_static_dh_params = 1;
+ }
+
+ if (gnutls_certificate_allocate_credentials(&cert_cred) < 0) {
+ fprintf(stderr, "memory error\n");
+ exit(1);
+ }
+
+ /* X509 credentials */
+ if (x509_cafile != NULL) {
+ if ((ret = gnutls_certificate_set_x509_trust_file
+ (cert_cred, x509_cafile, x509ctype)) < 0) {
+ fprintf(stderr, "Error reading '%s'\n",
+ x509_cafile);
+ GERR(ret);
+ exit(1);
+ } else {
+ printf("Processed %d CA certificate(s).\n", ret);
+ }
+ }
+ if (x509_crlfile != NULL) {
+ if ((ret = gnutls_certificate_set_x509_crl_file
+ (cert_cred, x509_crlfile, x509ctype)) < 0) {
+ fprintf(stderr, "Error reading '%s'\n",
+ x509_crlfile);
+ GERR(ret);
+ exit(1);
+ } else {
+ printf("Processed %d CRL(s).\n", ret);
+ }
+ }
+
+ if (x509_certfile_size > 0 && x509_keyfile_size > 0) {
+ for (i = 0; i < x509_certfile_size; i++) {
+ ret = gnutls_certificate_set_x509_key_file
+ (cert_cred, x509_certfile[i], x509_keyfile[i], x509ctype);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Error reading '%s' or '%s'\n",
+ x509_certfile[i], x509_keyfile[i]);
+ GERR(ret);
+ exit(1);
+ } else
+ cert_set = 1;
+ }
+ }
+
+ /* Raw public-key credentials */
+ if (rawpk_file_size > 0 && rawpk_keyfile_size > 0) {
+ for (i = 0; i < rawpk_keyfile_size; i++) {
+ ret = gnutls_certificate_set_rawpk_key_file(cert_cred, rawpk_file[i],
+ rawpk_keyfile[i],
+ x509ctype,
+ NULL, 0, NULL, 0,
+ 0, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Error reading '%s' or '%s'\n",
+ rawpk_file[i], rawpk_keyfile[i]);
+ GERR(ret);
+ exit(1);
+ } else {
+ cert_set = 1;
+ }
+ }
+ }
+
+ if (cert_set == 0) {
+ fprintf(stderr,
+ "Warning: no private key and certificate pairs were set.\n");
+ }
+
+#ifndef ENABLE_OCSP
+ if (HAVE_OPT(IGNORE_OCSP_RESPONSE_ERRORS) || ocsp_responses_size != 0) {
+ fprintf(stderr, "OCSP is not supported!\n");
+ exit(1);
+ }
+#else
+ /* OCSP status-request TLS extension */
+ if (HAVE_OPT(IGNORE_OCSP_RESPONSE_ERRORS))
+ gnutls_certificate_set_flags(cert_cred, GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK);
+
+ for (i = 0; i < ocsp_responses_size; i++ ) {
+ ret = gnutls_certificate_set_ocsp_status_request_file
+ (cert_cred, ocsp_responses[i], 0);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Cannot set OCSP status request file: %s: %s\n",
+ ocsp_responses[i],
+ gnutls_strerror(ret));
+ exit(1);
+ }
+ }
+#endif
+
+ if (use_static_dh_params) {
+#if defined(ENABLE_DHE) || defined(ENABLE_ANON)
+ ret = gnutls_certificate_set_known_dh_params(cert_cred, GNUTLS_SEC_PARAM_MEDIUM);
+ if (ret < 0) {
+ fprintf(stderr, "Error while setting DH parameters: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+#else
+ fprintf(stderr, "Setting DH parameters is not supported\n");
+ exit(1);
+#endif
+ } else {
+ gnutls_certificate_set_params_function(cert_cred, get_params);
+ }
+
+ /* this is a password file (created with the included srpcrypt utility)
+ * Read README.crypt prior to using SRP.
+ */
+#ifdef ENABLE_SRP
+ if (srp_passwd != NULL) {
+ gnutls_srp_allocate_server_credentials(&srp_cred);
+
+ if ((ret =
+ gnutls_srp_set_server_credentials_file(srp_cred,
+ srp_passwd,
+ srp_passwd_conf))
+ < 0) {
+ /* only exit is this function is not disabled
+ */
+ fprintf(stderr,
+ "Error while setting SRP parameters\n");
+ GERR(ret);
+ }
+ }
+#endif
+
+ /* this is a password file
+ */
+#ifdef ENABLE_PSK
+ if (psk_passwd != NULL) {
+ gnutls_psk_allocate_server_credentials(&psk_cred);
+
+ if ((ret =
+ gnutls_psk_set_server_credentials_file(psk_cred,
+ psk_passwd)) <
+ 0) {
+ /* only exit is this function is not disabled
+ */
+ fprintf(stderr,
+ "Error while setting PSK parameters\n");
+ GERR(ret);
+ }
+
+ if (HAVE_OPT(PSKHINT)) {
+ ret =
+ gnutls_psk_set_server_credentials_hint
+ (psk_cred, OPT_ARG(PSKHINT));
+ if (ret) {
+ fprintf(stderr,
+ "Error setting PSK identity hint.\n");
+ GERR(ret);
+ }
+ }
+
+ if (use_static_dh_params) {
+ ret = gnutls_psk_set_server_known_dh_params(psk_cred, GNUTLS_SEC_PARAM_MEDIUM);
+ if (ret < 0) {
+ fprintf(stderr, "Error while setting DH parameters: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ } else {
+ gnutls_psk_set_server_params_function(psk_cred,
+ get_params);
+ }
+ }
+#endif
+
+#ifdef ENABLE_ANON
+ gnutls_anon_allocate_server_credentials(&dh_cred);
+
+ if (use_static_dh_params) {
+ ret = gnutls_anon_set_server_known_dh_params(dh_cred, GNUTLS_SEC_PARAM_MEDIUM);
+ if (ret < 0) {
+ fprintf(stderr, "Error while setting DH parameters: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ } else {
+ gnutls_anon_set_server_params_function(dh_cred, get_params);
+ }
+#endif
+
+ if (noticket == 0)
+ gnutls_session_ticket_key_generate(&session_ticket_key);
+
+ if (earlydata) {
+ ret = gnutls_anti_replay_init(&anti_replay);
+ if (ret < 0) {
+ fprintf(stderr, "Error while initializing anti-replay: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ gnutls_anti_replay_set_add_function(anti_replay, anti_replay_db_add);
+ gnutls_anti_replay_set_ptr(anti_replay, NULL);
+ }
+
+ if (HAVE_OPT(MTU))
+ mtu = OPT_VALUE_MTU;
+ else
+ mtu = 1300;
+
+ if (HAVE_OPT(PORT))
+ port = OPT_VALUE_PORT;
+ else
+ port = 5556;
+
+ if (HAVE_OPT(UDP))
+ udp_server(name, port, mtu);
+ else
+ tcp_server(name, port);
+
+ return 0;
+}
+
+static void retry_handshake(listener_item *j)
+{
+ int r, ret;
+
+ r = gnutls_handshake(j->tls_session);
+ if (r < 0 && gnutls_error_is_fatal(r) == 0) {
+ check_alert(j->tls_session, r);
+ /* nothing */
+ } else if (r < 0) {
+ j->http_state = HTTP_STATE_CLOSING;
+ check_alert(j->tls_session, r);
+ fprintf(stderr, "Error in handshake: %s\n", gnutls_strerror(r));
+
+ do {
+ ret = gnutls_alert_send_appropriate(j->tls_session, r);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ j->close_ok = 0;
+ } else if (r == 0) {
+ if (gnutls_session_is_resumed(j->tls_session) != 0 && verbose != 0)
+ printf("*** This is a resumed session\n");
+
+ if (verbose != 0) {
+#if 0
+ printf("- connection from %s\n",
+ human_addr((struct sockaddr *)
+ &client_address,
+ calen,
+ topbuf,
+ sizeof(topbuf)));
+#endif
+
+ print_info(j->tls_session, verbose, verbose);
+
+ if (HAVE_OPT(KEYMATEXPORT))
+ print_key_material(j->tls_session,
+ OPT_ARG(KEYMATEXPORT),
+ HAVE_OPT(KEYMATEXPORTSIZE) ?
+ OPT_VALUE_KEYMATEXPORTSIZE :
+ 20);
+ }
+
+ j->close_ok = 1;
+ j->handshake_ok = 1;
+ }
+}
+
+static void try_rehandshake(listener_item *j)
+{
+ int r, ret;
+ fprintf(stderr, "*** Received hello message\n");
+
+ do {
+ r = gnutls_handshake(j->tls_session);
+ } while (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN);
+
+ if (r < 0) {
+ do {
+ ret = gnutls_alert_send_appropriate(j->tls_session, r);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ fprintf(stderr, "Error in rehandshake: %s\n", gnutls_strerror(r));
+ j->http_state = HTTP_STATE_CLOSING;
+ } else {
+ j->close_ok = 1;
+ j->http_state = HTTP_STATE_REQUEST;
+ }
+}
+
+static void tcp_server(const char *name, int port)
+{
+ int n, s;
+ char topbuf[512];
+ int accept_fd;
+ struct sockaddr_storage client_address;
+ socklen_t calen;
+ struct timeval tv;
+
+ s = listen_socket(name, port, SOCK_STREAM);
+ if (s < 0)
+ exit(1);
+
+ for (;;) {
+ gl_list_iterator_t iter;
+ gl_list_node_t node;
+ const void *elt;
+ gl_list_t accepted_list = gl_list_create_empty(GL_LINKED_LIST,
+ NULL, NULL, NULL,
+ true);
+ fd_set rd, wr;
+ time_t now = time(0);
+#ifndef _WIN32
+ int val;
+#endif
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ n = 0;
+
+/* flag which connections we are reading or writing to within the fd sets */
+ iter = gl_list_iterator(listener_list);
+ while (gl_list_iterator_next(&iter, &elt, &node)) {
+ listener_item *j = (listener_item *)elt;
+
+#ifndef _WIN32
+ val = fcntl(j->fd, F_GETFL, 0);
+ if ((val == -1)
+ || (fcntl(j->fd, F_SETFL, val | O_NONBLOCK) <
+ 0)) {
+ perror("fcntl()");
+ exit(1);
+ }
+#endif
+ if (j->start != 0 && now - j->start > 30) {
+ if (verbose != 0) {
+ fprintf(stderr, "Scheduling inactive connection for close\n");
+ }
+ j->http_state = HTTP_STATE_CLOSING;
+ }
+
+ if (j->listen_socket) {
+ FD_SET(j->fd, &rd);
+ n = MAX(n, j->fd);
+ }
+ if (j->http_state == HTTP_STATE_REQUEST) {
+ FD_SET(j->fd, &rd);
+ n = MAX(n, j->fd);
+ }
+ if (j->http_state == HTTP_STATE_RESPONSE) {
+ FD_SET(j->fd, &wr);
+ n = MAX(n, j->fd);
+ }
+ gl_list_node_set_value(listener_list, node, j);
+ }
+ gl_list_iterator_free(&iter);
+
+/* core operation */
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ n = select(n + 1, &rd, &wr, NULL, &tv);
+ if (n == -1 && errno == EINTR)
+ continue;
+ if (n < 0) {
+ perror("select()");
+ exit(1);
+ }
+
+/* read or write to each connection as indicated by select()'s return argument */
+ iter = gl_list_iterator(listener_list);
+ while (gl_list_iterator_next(&iter, &elt, &node)) {
+ listener_item *j = (listener_item *)elt;
+
+ /* a new connection has arrived */
+ if (FD_ISSET(j->fd, &rd) && j->listen_socket) {
+ calen = sizeof(client_address);
+ memset(&client_address, 0, calen);
+ accept_fd =
+ accept(j->fd,
+ (struct sockaddr *)
+ &client_address, &calen);
+
+ if (accept_fd < 0) {
+ perror("accept()");
+ } else {
+ char timebuf[SIMPLE_CTIME_BUF_SIZE];
+ time_t tt = time(0);
+ char *ctt;
+ listener_item *jj;
+
+ /* new list entry for the connection */
+ jj = xzalloc(sizeof(*jj));
+ gl_list_add_last(accepted_list, jj);
+ jj->http_request =
+ (char *) strdup("");
+ jj->http_state = HTTP_STATE_REQUEST;
+ jj->fd = accept_fd;
+ jj->start = tt;
+
+ jj->tls_session = initialize_session(0);
+ gnutls_session_set_ptr(jj->tls_session, jj);
+ gnutls_transport_set_int
+ (jj->tls_session, accept_fd);
+ set_read_funcs(jj->tls_session);
+ jj->handshake_ok = 0;
+ jj->close_ok = 0;
+
+ if (verbose != 0) {
+ ctt = simple_ctime(&tt, timebuf);
+ ctt[strlen(ctt) - 1] = 0;
+
+ printf
+ ("\n* Accepted connection from %s on %s\n",
+ human_addr((struct
+ sockaddr
+ *)
+ &client_address,
+ calen,
+ topbuf,
+ sizeof
+ (topbuf)),
+ ctt);
+ }
+ }
+ }
+
+ if (FD_ISSET(j->fd, &rd) && !j->listen_socket) {
+/* read partial GET request */
+ char buf[16*1024];
+ int r;
+
+ if (j->handshake_ok == 0) {
+ retry_handshake(j);
+ }
+
+ if (j->handshake_ok == 1) {
+ int earlydata_read = 0;
+ if (earlydata && !j->earlydata_eof) {
+ r = gnutls_record_recv_early_data(j->
+ tls_session,
+ buf,
+ MIN(sizeof(buf),
+ SMALL_READ_TEST));
+ if (r == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ j->earlydata_eof = 1;
+ }
+ if (r == 0) {
+ earlydata_read = 1;
+ }
+ }
+ if (!earlydata_read) {
+ r = gnutls_record_recv(j->
+ tls_session,
+ buf,
+ MIN(sizeof(buf),
+ SMALL_READ_TEST));
+ }
+ if (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) {
+ /* do nothing */
+ } else if (r <= 0) {
+ if (r == GNUTLS_E_HEARTBEAT_PING_RECEIVED) {
+ gnutls_heartbeat_pong(j->tls_session, 0);
+ } else if (r == GNUTLS_E_REHANDSHAKE) {
+ try_rehandshake(j);
+ } else {
+ j->http_state = HTTP_STATE_CLOSING;
+ if (r < 0) {
+ int ret;
+ check_alert(j->tls_session, r);
+ fprintf(stderr,
+ "Error while receiving data\n");
+ do {
+ ret = gnutls_alert_send_appropriate(j->tls_session, r);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ GERR(r);
+ j->close_ok = 0;
+ }
+ }
+ } else {
+ j->http_request =
+ realloc(j->
+ http_request,
+ j->
+ request_length
+ + r + 1);
+ if (j->http_request !=
+ NULL) {
+ memcpy(j->
+ http_request
+ +
+ j->
+ request_length,
+ buf, r);
+ j->request_length
+ += r;
+ j->http_request[j->
+ request_length]
+ = '\0';
+ } else {
+ j->http_state =
+ HTTP_STATE_CLOSING;
+ }
+ }
+/* check if we have a full HTTP header */
+
+ j->http_response = NULL;
+ if (j->http_state == HTTP_STATE_REQUEST && j->http_request != NULL) {
+ if ((http == 0
+ && strchr(j->
+ http_request,
+ '\n'))
+ || strstr(j->
+ http_request,
+ "\r\n\r\n")
+ || strstr(j->
+ http_request,
+ "\n\n")) {
+ if (get_response(j->
+ tls_session,
+ j->
+ http_request,
+ &j->
+ http_response,
+ &j->
+ response_length)) {
+ j->http_state =
+ HTTP_STATE_RESPONSE;
+ j->response_written
+ = 0;
+ } else {
+ j->http_state = HTTP_STATE_CLOSING;
+ }
+ }
+ }
+ }
+ }
+
+ if (FD_ISSET(j->fd, &wr)) {
+/* write partial response request */
+ int r;
+
+ if (j->handshake_ok == 0) {
+ retry_handshake(j);
+ }
+
+ if (j->handshake_ok == 1 && j->http_response == NULL) {
+ j->http_state = HTTP_STATE_CLOSING;
+ } else if (j->handshake_ok == 1 && j->http_response != NULL) {
+ r = gnutls_record_send(j->tls_session,
+ j->http_response
+ +
+ j->response_written,
+ MIN(j->response_length
+ -
+ j->response_written,
+ SMALL_READ_TEST));
+ if (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) {
+ /* do nothing */
+ } else if (r <= 0) {
+ j->http_state = HTTP_STATE_CLOSING;
+ if (r < 0) {
+ fprintf(stderr,
+ "Error while sending data\n");
+ GERR(r);
+ }
+ check_alert(j->tls_session,
+ r);
+ } else {
+ j->response_written += r;
+/* check if we have written a complete response */
+ if (j->response_written ==
+ j->response_length) {
+ if (http != 0)
+ j->http_state = HTTP_STATE_CLOSING;
+ else {
+ j->http_state = HTTP_STATE_REQUEST;
+ free(j->
+ http_response);
+ j->http_response = NULL;
+ j->response_length = 0;
+ j->request_length = 0;
+ j->http_request[0] = 0;
+ }
+ }
+ }
+ } else {
+ j->request_length = 0;
+ j->http_request[0] = 0;
+ j->http_state = HTTP_STATE_REQUEST;
+ }
+ }
+ gl_list_node_set_value(listener_list, node, j);
+ }
+ gl_list_iterator_free(&iter);
+
+/* loop through all connections, closing those that are in error */
+ iter = gl_list_iterator(listener_list);
+ while (gl_list_iterator_next(&iter, &elt, &node)) {
+ const listener_item *j = elt;
+
+ if (j->http_state == HTTP_STATE_CLOSING) {
+ gl_list_remove_node(listener_list, node);
+ }
+ }
+ gl_list_iterator_free(&iter);
+
+ iter = gl_list_iterator(accepted_list);
+ while (gl_list_iterator_next(&iter, &elt, &node)) {
+ gl_list_add_last(listener_list, elt);
+ }
+ gl_list_iterator_free(&iter);
+ gl_list_free(accepted_list);
+ }
+
+
+ gnutls_certificate_free_credentials(cert_cred);
+
+#ifdef ENABLE_SRP
+ if (srp_cred)
+ gnutls_srp_free_server_credentials(srp_cred);
+#endif
+
+#ifdef ENABLE_PSK
+ if (psk_cred)
+ gnutls_psk_free_server_credentials(psk_cred);
+#endif
+
+#ifdef ENABLE_ANON
+ gnutls_anon_free_server_credentials(dh_cred);
+#endif
+
+ if (noticket == 0)
+ gnutls_free(session_ticket_key.data);
+
+ if (earlydata)
+ gnutls_anti_replay_deinit(anti_replay);
+
+ if (nodb == 0)
+ wrap_db_deinit();
+ gnutls_global_deinit();
+
+}
+
+static void cmd_parser(int argc, char **argv)
+{
+ optionProcess(&gnutls_servOptions, argc, argv);
+
+ disable_client_cert = HAVE_OPT(DISABLE_CLIENT_CERT);
+ require_cert = ENABLED_OPT(REQUIRE_CLIENT_CERT);
+ if (HAVE_OPT(DEBUG))
+ debug = OPT_VALUE_DEBUG;
+
+ if (HAVE_OPT(QUIET))
+ verbose = 0;
+
+ if (HAVE_OPT(PRIORITY))
+ priorities = OPT_ARG(PRIORITY);
+
+ if (HAVE_OPT(LIST)) {
+ print_list(priorities, verbose);
+ exit(0);
+ }
+
+ nodb = HAVE_OPT(NODB);
+ noticket = HAVE_OPT(NOTICKET);
+ earlydata = HAVE_OPT(EARLYDATA);
+
+ if (HAVE_OPT(ECHO)) {
+ http = 0;
+ if (HAVE_OPT(CRLF))
+ strip_crlf = 0;
+ } else
+ http = 1;
+
+ record_max_size = OPT_VALUE_RECORDSIZE;
+
+ if (HAVE_OPT(X509FMTDER))
+ x509ctype = GNUTLS_X509_FMT_DER;
+ else
+ x509ctype = GNUTLS_X509_FMT_PEM;
+
+ generate = HAVE_OPT(GENERATE);
+
+ if (HAVE_OPT(DHPARAMS))
+ dh_params_file = OPT_ARG(DHPARAMS);
+
+ if (HAVE_OPT(ALPN)) {
+ alpn_protos = STACKLST_OPT(ALPN);
+ alpn_protos_size = STACKCT_OPT(ALPN);
+ }
+
+ if (HAVE_OPT(X509KEYFILE)) {
+ x509_keyfile = STACKLST_OPT(X509KEYFILE);
+ x509_keyfile_size = STACKCT_OPT(X509KEYFILE);
+ }
+
+ if (HAVE_OPT(X509CERTFILE)) {
+ x509_certfile = STACKLST_OPT(X509CERTFILE);
+ x509_certfile_size = STACKCT_OPT(X509CERTFILE);
+ }
+
+ if (x509_certfile_size != x509_keyfile_size) {
+ fprintf(stderr, "The certificate number provided (%u) doesn't match the keys (%u)\n",
+ x509_certfile_size, x509_keyfile_size);
+ exit(1);
+ }
+
+ if (HAVE_OPT(X509CAFILE))
+ x509_cafile = OPT_ARG(X509CAFILE);
+ if (HAVE_OPT(X509CRLFILE))
+ x509_crlfile = OPT_ARG(X509CRLFILE);
+
+ if (HAVE_OPT(RAWPKKEYFILE)) {
+ rawpk_keyfile = STACKLST_OPT(RAWPKKEYFILE);
+ rawpk_keyfile_size = STACKCT_OPT(RAWPKKEYFILE);
+ }
+
+ if (HAVE_OPT(RAWPKFILE)) {
+ rawpk_file = STACKLST_OPT(RAWPKFILE);
+ rawpk_file_size = STACKCT_OPT(RAWPKFILE);
+ }
+
+ if (rawpk_file_size != rawpk_keyfile_size) {
+ fprintf(stderr, "The number of raw public-keys provided (%u) doesn't match the number of corresponding private keys (%u)\n",
+ rawpk_file_size, rawpk_keyfile_size);
+ exit(1);
+ }
+
+ if (HAVE_OPT(SRPPASSWD))
+ srp_passwd = OPT_ARG(SRPPASSWD);
+ if (HAVE_OPT(SRPPASSWDCONF))
+ srp_passwd_conf = OPT_ARG(SRPPASSWDCONF);
+
+ if (HAVE_OPT(PSKPASSWD))
+ psk_passwd = OPT_ARG(PSKPASSWD);
+
+ if (HAVE_OPT(OCSP_RESPONSE)) {
+ ocsp_responses = STACKLST_OPT(OCSP_RESPONSE);
+ ocsp_responses_size = STACKCT_OPT(OCSP_RESPONSE);
+ }
+
+ if (HAVE_OPT(SNI_HOSTNAME))
+ sni_hostname = OPT_ARG(SNI_HOSTNAME);
+
+ if (HAVE_OPT(SNI_HOSTNAME_FATAL))
+ sni_hostname_fatal = 1;
+
+ if (HAVE_OPT(HTTPDATA))
+ http_data_file = OPT_ARG(HTTPDATA);
+
+}
+
+/* session resuming support */
+
+#define SESSION_ID_SIZE 128
+#define SESSION_DATA_SIZE (16*1024)
+
+typedef struct {
+ unsigned char session_id[SESSION_ID_SIZE];
+ unsigned int session_id_size;
+
+ gnutls_datum_t session_data;
+} CACHE;
+
+static CACHE *cache_db;
+static int cache_db_ptr;
+static int cache_db_alloc;
+
+static void wrap_db_init(void)
+{
+}
+
+static void wrap_db_deinit(void)
+{
+ int i;
+
+ for (i = 0; i < cache_db_ptr; i++)
+ free(cache_db[i].session_data.data);
+ free(cache_db);
+}
+
+static int
+wrap_db_store(void *dbf, gnutls_datum_t key, gnutls_datum_t data)
+{
+ int i;
+ time_t now = time(0);
+
+ if (key.size > SESSION_ID_SIZE)
+ return GNUTLS_E_DB_ERROR;
+ if (data.size > SESSION_DATA_SIZE)
+ return GNUTLS_E_DB_ERROR;
+
+ if (cache_db_ptr < cache_db_alloc)
+ i = cache_db_ptr++;
+ else {
+ /* find empty or expired slot to store the new entry */
+ for (i = 0; i < cache_db_ptr; i++)
+ if (cache_db[i].session_id_size == 0 ||
+ !(now <
+ gnutls_db_check_entry_expire_time(&cache_db[i].
+ session_data)))
+ break;
+
+ if (i == cache_db_ptr) {
+ /* try to allocate additional slots */
+ if (cache_db_ptr == ssl_session_cache) {
+ fprintf(stderr,
+ "Error: too many sessions\n");
+ return GNUTLS_E_DB_ERROR;
+ }
+ cache_db_alloc = cache_db_alloc * 2 + 1;
+ cache_db = realloc(cache_db,
+ cache_db_alloc * sizeof(CACHE));
+ if (!cache_db)
+ return GNUTLS_E_MEMORY_ERROR;
+ memset(cache_db + cache_db_ptr, 0,
+ (cache_db_alloc - cache_db_ptr) * sizeof(CACHE));
+ cache_db_ptr++;
+ }
+ }
+
+ memcpy(cache_db[i].session_id, key.data, key.size);
+ cache_db[i].session_id_size = key.size;
+
+ /* resize the data slot if needed */
+ if (cache_db[i].session_data.size < data.size) {
+ cache_db[i].session_data.data =
+ realloc(cache_db[i].session_data.data,
+ data.size);
+ if (!cache_db[i].session_data.data)
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ memcpy(cache_db[i].session_data.data, data.data, data.size);
+ cache_db[i].session_data.size = data.size;
+
+ return 0;
+}
+
+static gnutls_datum_t wrap_db_fetch(void *dbf, gnutls_datum_t key)
+{
+ gnutls_datum_t res = { NULL, 0 };
+ time_t now = time(0);
+ int i;
+
+ for (i = 0; i < cache_db_ptr; i++) {
+ if (key.size == cache_db[i].session_id_size &&
+ memcmp(key.data, cache_db[i].session_id,
+ key.size) == 0 &&
+ now < gnutls_db_check_entry_expire_time(&cache_db[i].
+ session_data)) {
+ res.size = cache_db[i].session_data.size;
+
+ res.data = malloc(res.size);
+ if (res.data == NULL)
+ return res;
+
+ memcpy(res.data, cache_db[i].session_data.data,
+ res.size);
+
+ return res;
+ }
+ }
+ return res;
+}
+
+static int wrap_db_delete(void *dbf, gnutls_datum_t key)
+{
+ int i;
+
+ for (i = 0; i < cache_db_ptr; i++) {
+ if (key.size == cache_db[i].session_id_size &&
+ memcmp(key.data, cache_db[i].session_id,
+ key.size) == 0) {
+
+ cache_db[i].session_id_size = 0;
+ free(cache_db[i].session_data.data);
+ cache_db[i].session_data.data = NULL;
+ cache_db[i].session_data.size = 0;
+
+ return 0;
+ }
+ }
+
+ return GNUTLS_E_DB_ERROR;
+}
+
+static int
+anti_replay_db_add(void *dbf, time_t exp, const gnutls_datum_t *key, const gnutls_datum_t *data)
+{
+ time_t now = time(0);
+ int i;
+
+ for (i = 0; i < cache_db_ptr; i++) {
+ if (key->size == cache_db[i].session_id_size &&
+ memcmp(key->data, cache_db[i].session_id,
+ key->size) == 0 &&
+ now < gnutls_db_check_entry_expire_time(&cache_db[i].
+ session_data))
+ return GNUTLS_E_DB_ENTRY_EXISTS;
+ }
+
+ return wrap_db_store(dbf, *key, *data);
+}