summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2011-08-13 14:10:55 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2011-08-13 14:15:05 +0200
commit2385c7f999c12802b11859a34b89ff7662b1f4af (patch)
treea6dc75e9438f5ea2413a627ce8dde5d28e2a195b /src
parent7f373a9e760c1aaad40aff7878dba1399831c9c3 (diff)
downloadgnutls-2385c7f999c12802b11859a34b89ff7662b1f4af.tar.gz
Added crywrap to the distributed programs.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/crywrap/Makefile.am29
-rw-r--r--src/crywrap/README2
-rw-r--r--src/crywrap/crywrap.c1119
-rw-r--r--src/crywrap/crywrap.h105
-rw-r--r--src/crywrap/primes.h42
6 files changed, 1302 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 80b0a0ffee..10813cf1e3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,7 +18,11 @@
# along with this file; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-SUBDIRS = cfg
+SUBDIRS = cfg
+
+if ENABLE_CRYWRAP
+SUBDIRS += crywrap
+endif
AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
AM_CPPFLAGS = \
diff --git a/src/crywrap/Makefile.am b/src/crywrap/Makefile.am
new file mode 100644
index 0000000000..e5bd90d8e5
--- /dev/null
+++ b/src/crywrap/Makefile.am
@@ -0,0 +1,29 @@
+## Process this file with automake to produce Makefile.in
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# This file 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.
+#
+# This file 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 file; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+AM_CFLAGS = $(WARN_CFLAGS)
+AM_CPPFLAGS = \
+ -I$(srcdir)/../../gl \
+ -I$(builddir)/../../lib/includes \
+ -I$(srcdir)/../../lib/includes \
+ -DSYSCONFDIR=\"${sysconfdir}\" \
+ -DCRYWRAP_PATCHLEVEL=\"${CRYWRAP_PATCHLEVEL}\"
+
+bin_PROGRAMS = crywrap
+
+crywrap_SOURCES = crywrap.c primes.h #compat.h compat.c
+crywrap_LDADD = ../../lib/libgnutls.la ../../gl/libgnu.la -lidn
diff --git a/src/crywrap/README b/src/crywrap/README
new file mode 100644
index 0000000000..9408304833
--- /dev/null
+++ b/src/crywrap/README
@@ -0,0 +1,2 @@
+The crywrap program by Gergely Nagy is not part of the GnuTLS library, but is
+distributed with GnuTLS.
diff --git a/src/crywrap/crywrap.c b/src/crywrap/crywrap.c
new file mode 100644
index 0000000000..3c1b6e87e4
--- /dev/null
+++ b/src/crywrap/crywrap.c
@@ -0,0 +1,1119 @@
+/* -*- mode: c; c-file-style: "gnu" -*-
+ * crywrap.c -- CryWrap
+ * Copyright (C) 2003, 2004 Gergely Nagy <algernon@bonehunter.rulez.org>
+ * Copyright (C) 2011 Nikos Mavrogiannopoulos
+ *
+ * This file is part of CryWrap.
+ *
+ * CryWrap 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.
+ *
+ * CryWrap 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** @file crywrap.c
+ * CryWrap itself.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_ARGP_H
+#include <argp.h>
+#endif
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <grp.h>
+#include <idna.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringprep.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+/* Gnulib portability files. */
+#include "progname.h"
+#include "argp.h"
+
+#include "crywrap.h"
+#include "primes.h"
+
+static int system_log(const char* fmt, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+;
+
+static int system_log_error(const char* fmt, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+;
+
+static int debug_log(const char* fmt, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+;
+
+typedef int (*cry_log_func)(const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 1, 2)))
+#endif
+;
+
+static cry_log_func cry_log = system_log;
+static cry_log_func cry_error = system_log_error;
+
+/** @defgroup globals Global variables.
+ * @{
+ */
+/** Status flag to toggle on SIGCHLD.
+ */
+static sig_atomic_t sigchld = 0;
+/** An array of pids.
+ * This array holds the PIDs of all of our children, indexed by the
+ * socket the associated client connected to us.
+ */
+static pid_t crywrap_children[_CRYWRAP_MAXCONN + 2];
+static pid_t main_pid = -1; /**< Pid of the main process */
+static const char *pidfile = _CRYWRAP_PIDFILE; /**< File to log our PID
+ into. */
+/** GNUTLS server credentials.
+ */
+static gnutls_certificate_server_credentials cred;
+static gnutls_dh_params dh_params; /**< GNUTLS DH parameters. */
+static const int dh_bits = 1024; /**< GNUTLS DH bits. */
+
+/** Bugreport address.
+ * Used by the argp suite.
+ */
+const char *argp_program_bug_address = "<bugs-gnutls@gnu.org>";
+/** Porgram version.
+ * Used by the argp suite.
+ */
+const char *argp_program_version = __CRYWRAP__ " " _CRYWRAP_VERSION;
+
+/** The options CryWrap takes.
+ * Used by the argp suite.
+ */
+static const struct argp_option _crywrap_options[] = {
+ {NULL, 0, NULL, 0, "Mandatory options:", 1},
+ {"destination", 'd', "IP/PORT", 0, "IP and port to connect to", 1},
+ {NULL, 0, NULL, 0, "TLS certificates:", 2},
+ {"pem", 'p', "TYPE=PATH", 0, "Server key and certificate", 2},
+ {"anon", 'a', NULL, 0, "Enable Anon-DH (don't use a certificate)", 2},
+ {"verify", 'v', "LEVEL", OPTION_ARG_OPTIONAL,
+ "Verify clients certificate", 2},
+ {NULL, 0, NULL, 0, "Other options:", 3},
+ {"user", 'u', "UID", 0, "User ID to run as", 3},
+ {"pidfile", 'P', "PATH", 0, "File to log the PID into", 3},
+ {"inetd", 'i', NULL, 0, "Enable inetd mode", 3},
+ {"listen", 'l', "IP/PORT", 0, "IP and port to listen on", 3},
+ {"debug", 'D', NULL, 0, "Do not fork", 2},
+ {0, 0, 0, 0, NULL, 0}
+};
+
+static error_t _crywrap_config_parse_opt (int key, char *arg,
+ struct argp_state *state);
+/** The main argp structure for Crywrap.
+ */
+static const struct argp _crywrap_argp =
+ {_crywrap_options, _crywrap_config_parse_opt, 0,
+ __CRYWRAP__ " -- Security for the masses\v"
+ "The --destination option is mandatory, as is --listen if --inetd "
+ "was not used.",
+ NULL, NULL, NULL};
+
+#ifndef __DOXYGEN__
+enum
+{
+ CRYWRAP_P_SUBOPT_CERT,
+ CRYWRAP_P_SUBOPT_KEY,
+ CRYWRAP_P_SUBOPT_END
+};
+#endif
+
+/** Helper structure for parsing --pem subopts.
+ */
+static char * const _crywrap_p_subopts[] = {
+ [CRYWRAP_P_SUBOPT_CERT] = (char*)"cert",
+ [CRYWRAP_P_SUBOPT_KEY] = (char*)"key",
+ [CRYWRAP_P_SUBOPT_END] = NULL
+};
+
+/** Helper variable to set if a certificate was explictly set.
+ */
+static int cert_seen = 0;
+
+/** @} */
+
+/* Forward declaration */
+static int _crywrap_dh_params_generate (void);
+
+/** @defgroup signal Signal handlers & co.
+ * @{
+ */
+
+/** SIGCHLD handler
+ */
+static void
+_crywrap_sigchld_handler (int sig)
+{
+ sigchld = 1;
+ signal (sig, _crywrap_sigchld_handler);
+}
+
+/** SIGHUP handler.
+ * Regenerates DH and RSA paramaters. Takes a bit long...
+ */
+static void
+_crywrap_sighup_handler (int sig)
+{
+ _crywrap_dh_params_generate ();
+
+ gnutls_certificate_set_dh_params (cred, dh_params);
+
+ signal (sig, _crywrap_sighup_handler);
+}
+
+/** Generic signal handler.
+ * This one removes the #pidfile, if necessary.
+ */
+static void
+_crywrap_sighandler (int sig)
+{
+ if (getpid () == main_pid)
+ {
+ cry_log ("Exiting on signal %d", sig);
+ if (pidfile && *pidfile)
+ unlink (pidfile);
+ closelog ();
+ exit (0);
+ }
+}
+/** @} */
+
+/** @defgroup parsing Option parsing
+ * @{
+ */
+
+/** Service resolver.
+ * Resolves a service - be it a name or a number.
+ *
+ * @param serv is the port to resolve.
+ *
+ * @returns The purt number, or -1 on error.
+ */
+static int
+_crywrap_port_get (const char *serv)
+{
+ int port;
+ struct servent *se;
+
+ if (!serv)
+ return -1;
+
+ se = getservbyname (serv, "tcp");
+ if (!se)
+ port = atoi (serv);
+ else
+ port = ntohs (se->s_port);
+
+ return port;
+}
+
+/** Address resolver.
+ * Resolves an address - be it numeric or a hostname, IPv4 or IPv6.
+ *
+ * @param hostname is the host to resolve.
+ * @param addr is the structure to put the result into.
+ *
+ * @returns Zero on success, -1 on error.
+ */
+static int
+_crywrap_addr_get (const char *hostname, struct sockaddr_storage **addr)
+{
+ struct addrinfo *res;
+ struct addrinfo hints;
+ ssize_t len;
+ char *lz = NULL;
+
+ if (idna_to_ascii_lz (hostname, &lz, 0) != IDNA_SUCCESS)
+ return -1;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_IP;
+ *addr = calloc (1, sizeof (struct sockaddr_storage));
+
+ if (getaddrinfo (lz, NULL, &hints, &res) != 0)
+ {
+ free (lz);
+ return -1;
+ }
+
+ free (lz);
+
+ switch (res->ai_addr->sa_family)
+ {
+ case AF_INET:
+ len = sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ len = sizeof (struct sockaddr_in6);
+ break;
+ default:
+ freeaddrinfo (res);
+ return -1;
+ }
+
+ if (len < (ssize_t)res->ai_addrlen)
+ {
+ freeaddrinfo (res);
+ return -1;
+ }
+
+ memcpy (*addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo (res);
+
+ return 0;
+}
+
+/** Parse a HOST/IP pair.
+ * Splits up a given HOST/IP pair, and converts them into structures
+ * directly usable by libc routines.
+ *
+ * @param ip is the HOST/IP pair to parse.
+ * @param port is a pointer to an integer where the port number should
+ * go.
+ * @param addr is the destination of the resolved and parsed IP.
+ *
+ * @returns Zero on success, -1 on error.
+ */
+static int
+_crywrap_parse_ip (const char *ip, in_port_t *port,
+ struct sockaddr_storage **addr, char **host)
+{
+ char *s_ip;
+ char *tmp;
+
+ tmp = strchr (ip, '/');
+
+ if (!tmp)
+ return -1;
+
+ if (tmp == ip)
+ {
+ s_ip = strdup ("0.0.0.0");
+ *port = (in_port_t)_crywrap_port_get (&ip[1]);
+ }
+ else
+ {
+ *port = (in_port_t)_crywrap_port_get (&tmp[1]);
+ s_ip = strndup (ip, tmp - ip);
+ }
+
+ if (!*port)
+ return -1;
+
+ if (host)
+ *host = strdup (s_ip);
+
+ return _crywrap_addr_get (s_ip, addr);
+}
+
+/** Argument parsing routine.
+ * Used by the argp suite.
+ */
+static error_t
+_crywrap_config_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ crywrap_config_t *cfg = (crywrap_config_t *)state->input;
+ char *pem_cert, *pem_key, *subopts, *value;
+
+ switch (key)
+ {
+ case 'D':
+ cfg->debug = 1;
+ cry_log = debug_log;
+ cry_error = debug_log;
+ break;
+ case 'd':
+ if (_crywrap_parse_ip (arg, &cfg->dest.port, &cfg->dest.addr,
+ &cfg->dest.host) < 0)
+ argp_error (state, "Could not resolve address: `%s'", arg);
+ break;
+ case 'l':
+ if (_crywrap_parse_ip (arg, &cfg->listen.port,
+ &cfg->listen.addr, NULL) < 0)
+ argp_error (state, "Could not resolve address: `%s'", arg);
+ break;
+ case 'u':
+ cfg->uid = atoi (arg);
+ break;
+ case 'P':
+ if (arg && *arg)
+ cfg->pidfile = strdup (arg);
+ else
+ cfg->pidfile = NULL;
+ break;
+ case 'p':
+ subopts = optarg;
+ pem_cert = NULL;
+ pem_key = NULL;
+ while (*subopts != '\0')
+ switch (getsubopt (&subopts, _crywrap_p_subopts, &value))
+ {
+ case CRYWRAP_P_SUBOPT_CERT:
+ pem_cert = strdup (value);
+ break;
+ case CRYWRAP_P_SUBOPT_KEY:
+ pem_key = strdup (value);
+ break;
+ default:
+ pem_cert = strdup (value);
+ break;
+ }
+ if (!pem_key)
+ pem_key = strdup (pem_cert);
+ if (!pem_cert)
+ pem_cert = strdup (pem_key);
+ if (gnutls_certificate_set_x509_key_file (cred, pem_cert, pem_key,
+ GNUTLS_X509_FMT_PEM) < 0)
+ argp_error (state, "error reading X.509 key or certificate file.");
+ cert_seen = 1;
+ break;
+ case 'i':
+ cfg->inetd = 1;
+ break;
+ case 'a':
+ cfg->anon = 1;
+ cfg->verify = 0;
+ break;
+ case 'v':
+ cfg->verify = (arg) ? atoi (arg) : 1;
+ break;
+ case ARGP_KEY_END:
+ if (!cfg->inetd)
+ {
+ if (!cfg->listen.addr || !cfg->dest.addr)
+ argp_error
+ (state,
+ "a listening and a destination address must be set!");
+ }
+ else
+ if (!cfg->dest.addr)
+ argp_error (state, "a destination address must be set!");
+ if (cert_seen)
+ break;
+ if (cfg->anon)
+ break;
+ if (gnutls_certificate_set_x509_key_file (cred, _CRYWRAP_PEMFILE,
+ _CRYWRAP_PEMFILE,
+ GNUTLS_X509_FMT_PEM) < 0)
+ argp_error (state, "error reading X.509 key or certificate file.");
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/** Configuration parsing.
+ * Sets up the default values, and parses the command-line options
+ * using argp.
+ *
+ * @note Does not return if an error occurred.
+ */
+static crywrap_config_t *
+_crywrap_config_parse (int argc, char **argv)
+{
+ crywrap_config_t *config =
+ (crywrap_config_t *)malloc (sizeof (crywrap_config_t));
+
+ config->listen.port = 0;
+ config->listen.addr = NULL;
+ config->dest.port = 0;
+ config->dest.addr = NULL;
+
+ config->uid = _CRYWRAP_UID;
+ config->pidfile = _CRYWRAP_PIDFILE;
+ config->inetd = 0;
+ config->anon = 0;
+ config->verify = 1;
+
+ argp_parse (&_crywrap_argp, argc, argv, 0, 0, config);
+
+ return config;
+}
+/** @} */
+
+/** @defgroup tls Lower-level TLS routines.
+ * @{
+ */
+
+/** Create a GNUTLS session.
+ * Initialises the cyphers and the session database for a new TLS
+ * session.
+ *
+ * @returns The newly created TLS session.
+ */
+static gnutls_session
+_crywrap_tls_session_create (const crywrap_config_t *config)
+{
+ gnutls_session session;
+
+ gnutls_init (&session, GNUTLS_SERVER);
+
+
+ if (config->anon) {
+ gnutls_priority_set_direct(session, "NORMAL:+ANON-ECDH:+ANON-DH", NULL);
+ gnutls_credentials_set (session, GNUTLS_CRD_ANON, cred);
+ } else {
+ gnutls_priority_set_direct(session, "NORMAL", NULL);
+ gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, cred);
+ }
+
+ gnutls_dh_set_prime_bits (session, dh_bits);
+
+ gnutls_handshake_set_private_extensions (session, 1);
+
+ if (config->verify)
+ gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
+
+ return session;
+}
+
+/** (Re)Initialise Diffie Hellman parameters.
+ * @returns Zero.
+ */
+static int
+_crywrap_dh_params_generate (void)
+{
+ if (gnutls_dh_params_init (&dh_params) < 0)
+ {
+ cry_error ("%s", "Error in dh parameter initialisation.");
+ exit (3);
+ }
+
+ if (gnutls_dh_params_generate2 (dh_params, dh_bits) < 0)
+ {
+ cry_error ("%s", "Error in prime generation.");
+ exit (3);
+ }
+
+ gnutls_certificate_set_dh_params (cred, dh_params);
+
+ return 0;
+}
+
+/** Generate initial DH and RSA params.
+ * Loads the pre-generated DH primes.
+ */
+static void
+_crywrap_tls_init (void)
+{
+ gnutls_datum dh = { _crywrap_prime_dh_1024, sizeof(_crywrap_prime_dh_1024) };
+
+ gnutls_dh_params_init (&dh_params);
+ gnutls_dh_params_import_pkcs3 (dh_params, &dh, GNUTLS_X509_FMT_PEM);
+
+ gnutls_certificate_set_dh_params (cred, dh_params);
+}
+/** @} */
+
+/** @defgroup networking Networking
+ * @{
+ */
+
+/** Bind to an address.
+ * This one binds to an address, handles errors and anything that may
+ * arise.
+ *
+ * @param ai is the address information.
+ * @param listen_port is the port to bind to, and listen on.
+ *
+ * @returns The bound filedescriptor, or -1 on error.
+ */
+static int
+_crywrap_bind (const struct addrinfo *ai, int listen_port)
+{
+ int ret;
+ const int one = 1;
+ int listenfd;
+ char sock_name[NI_MAXHOST];
+
+ listenfd = socket (ai->ai_family, SOCK_STREAM, IPPROTO_IP);
+ if (listenfd == -1)
+ {
+ cry_error ("socket: %s", strerror (errno));
+ return -1;
+ }
+
+ memset (sock_name, 0, sizeof (sock_name));
+ getnameinfo ((struct sockaddr *)ai->ai_addr, ai->ai_addrlen, sock_name,
+ sizeof (sock_name), NULL, 0, NI_NUMERICHOST);
+
+ switch (ai->ai_family)
+ {
+ case AF_INET6:
+ ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_port = listen_port;
+ break;
+ case AF_INET:
+ ((struct sockaddr_in *)(ai->ai_addr))->sin_port = listen_port;
+ break;
+ }
+
+ ret = setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR,
+ &one, sizeof (one));
+ if (ret != 0)
+ {
+ cry_error ("setsockopt: %s (%s)", strerror (errno), sock_name);
+ return -1;
+ }
+
+ ret = bind (listenfd, ai->ai_addr, ai->ai_addrlen);
+ if (ret != 0)
+ {
+ cry_error ("bind to %s failed: %s", sock_name, strerror (errno));
+ return -1;
+ }
+
+ if (listen (listenfd, _CRYWRAP_MAXCONN) != 0)
+ {
+ cry_error ("listen on %s failed: %s", sock_name, strerror (errno));
+ return -1;
+ }
+
+ cry_log ("Socket bound to port %d on %s.", ntohs (listen_port), sock_name);
+
+ return listenfd;
+}
+
+/** Set up a listening socket.
+ * Sets up a listening socket on all the required addresses.
+ *
+ * @param config holds the CryWrap configuration, from where the
+ * listen address and port will be extracted.
+ *
+ * @returns The listening FD on success, -1 on error.
+ */
+static int
+_crywrap_listen (const crywrap_config_t *config)
+{
+ struct addrinfo *cur;
+ int ret;
+
+ cur = calloc (1, sizeof (struct addrinfo));
+ cur->ai_family = config->listen.addr->ss_family;
+
+ switch (cur->ai_family)
+ {
+ case AF_INET6:
+ cur->ai_addrlen = sizeof (struct sockaddr_in6);
+ break;
+ case AF_INET:
+ cur->ai_addrlen = sizeof (struct sockaddr_in);
+ break;
+ }
+
+ cur->ai_addr = malloc (cur->ai_addrlen);
+ memcpy (cur->ai_addr, config->listen.addr, cur->ai_addrlen);
+
+ ret = _crywrap_bind (cur, htons (config->listen.port));
+ free (cur->ai_addr);
+ free (cur);
+
+ return ret;
+}
+
+/** Connect to a remote server.
+ * Estabilishes a connection to a remote server, and handles all
+ * errors and anything that may arise during this process.
+ *
+ * @param addr is the address of the remote server.
+ * @param port is the port to connect to.
+ *
+ * @returns the connected socket on success, otherwise it exits.
+ */
+static int
+_crywrap_remote_connect (const struct sockaddr_storage *addr, int port)
+{
+ struct addrinfo *cur;
+ int sock;
+
+ cur = calloc (1, sizeof (struct addrinfo));
+ cur->ai_family = addr->ss_family;
+
+ switch (cur->ai_family)
+ {
+ case AF_INET6:
+ cur->ai_addrlen = sizeof (struct sockaddr_in6);
+ break;
+ case AF_INET:
+ cur->ai_addrlen = sizeof (struct sockaddr_in);
+ break;
+ }
+
+ cur->ai_addr = malloc (cur->ai_addrlen);
+ memcpy (cur->ai_addr, addr, cur->ai_addrlen);
+
+ switch (cur->ai_family)
+ {
+ case AF_INET6:
+ ((struct sockaddr_in6 *)(cur->ai_addr))->sin6_port = port;
+ break;
+ case AF_INET:
+ ((struct sockaddr_in *)(cur->ai_addr))->sin_port = port;
+ break;
+ }
+
+ sock = socket (cur->ai_family, SOCK_STREAM, IPPROTO_IP);
+ if (sock < 0)
+ {
+ cry_error ("socket(): %s", strerror (errno));
+ exit (1);
+ }
+
+ if (connect (sock, cur->ai_addr, cur->ai_addrlen) < 0)
+ {
+ cry_error ("connect(): %s", strerror (errno));
+ exit (1);
+ }
+
+ free (cur->ai_addr);
+ free (cur);
+
+ return sock;
+}
+
+/** @} */
+
+/** @defgroup crywrap Main CryWrap code.
+ * @{
+ */
+
+/** Drop privileges.
+ * Drop privileges, if running as root.
+ * Upon failure, it will make CryWrap exit.
+ */
+static void
+_crywrap_privs_drop (const crywrap_config_t *config)
+{
+ struct passwd *pwd;
+
+ if (getuid () != 0)
+ {
+ cry_log ("%s", "Not running as root, not dropping privileges.");
+ return;
+ }
+
+ if ((pwd = getpwuid (config->uid)) == NULL)
+ {
+ cry_error ("getpwuid(): %s", strerror (errno));
+ exit (1);
+ }
+
+ if (initgroups (pwd->pw_name, pwd->pw_gid) == -1)
+ {
+ cry_error ("initgroups(): %s", strerror (errno));
+ exit (1);
+ }
+
+ if (setgid (pwd->pw_gid) == -1)
+ {
+ cry_error ("setgid(): %s", strerror (errno));
+ exit (1);
+ }
+
+ if (setuid (config->uid))
+ {
+ cry_error ("setuid(): %s", strerror (errno));
+ exit (1);
+ }
+}
+
+/** Set up the PID file.
+ * Checks if a #pidfile already exists, and create one - containing the
+ * current PID - if one does not.
+ *
+ * @note Exits upon error.
+ */
+static void
+_crywrap_setup_pidfile (const crywrap_config_t *config)
+{
+ char mypid[128];
+ int pidfilefd;
+
+ if (!config->pidfile || !*(config->pidfile))
+ return;
+
+ if (!access (config->pidfile, F_OK))
+ {
+ cry_error ("Pidfile (%s) already exists. Exiting.", config->pidfile);
+ exit (1);
+ }
+ if ((pidfilefd = open (config->pidfile,
+ O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1)
+ {
+ cry_error ("Cannot create pidfile (%s): %s.\n", config->pidfile,
+ strerror (errno));
+ exit (1);
+ }
+ fchown (pidfilefd, config->uid, (gid_t)-1);
+
+ main_pid = getpid ();
+ snprintf (mypid, sizeof (mypid), "%d\n", main_pid);
+ write (pidfilefd, mypid, strlen (mypid));
+ close (pidfilefd);
+ pidfile = config->pidfile;
+}
+
+/** Child cleanup routine.
+ * Called after a SIGCHLD is received. Walks through #crywrap_children
+ * and closes the socket of the one that exited.
+ */
+static void
+_crywrap_reap_children (void)
+{
+ pid_t child;
+ int status, i;
+
+ while ((child = waitpid (-1, &status, WNOHANG)) > (pid_t) 0)
+ {
+ for (i = 0; i < _CRYWRAP_MAXCONN; i++)
+ {
+ if (!crywrap_children[i])
+ continue;
+ if (child == crywrap_children[i])
+ {
+ shutdown (i, SHUT_RDWR);
+ close (i);
+ crywrap_children[i] = 0;
+ }
+ }
+ }
+ sigchld = 0;
+}
+
+/** Handles one client.
+ * This one connects to the remote server, and proxies every traffic
+ * between our client and the server.
+ *
+ * @param config is the main CryWrap configuration structure.
+ * @param insock is the socket through which the client sends input.
+ * @param outsock is the socket through which we send output.
+ *
+ * @note Exits on error.
+ */
+static int
+_crywrap_do_one (const crywrap_config_t *config, int insock, int outsock)
+{
+ int sock, ret, tls_pending;
+ gnutls_session session;
+ char buffer[_CRYWRAP_MAXBUF + 2];
+ fd_set fdset;
+ unsigned int status = 0;
+ struct sockaddr_storage faddr;
+ socklen_t socklen = sizeof (struct sockaddr_storage);
+ char peer_name[NI_MAXHOST];
+
+ /* Log the connection */
+ if (getpeername (insock, (struct sockaddr *)&faddr, &socklen) != 0)
+ cry_error ("getpeername(): %s", strerror (errno));
+ else
+ {
+ getnameinfo ((struct sockaddr *)&faddr,
+ sizeof (struct sockaddr_storage), peer_name,
+ sizeof (peer_name), NULL, 0, NI_NUMERICHOST);
+ cry_log ("Accepted connection from %s on %d to %s/%d",
+ peer_name, insock, config->dest.host,
+ config->dest.port);
+ }
+
+ /* Do the handshake with our peer */
+ session = _crywrap_tls_session_create (config);
+ gnutls_transport_set_ptr2 (session,
+ (gnutls_transport_ptr_t)insock,
+ (gnutls_transport_ptr_t)outsock);
+
+ do
+ {
+ ret = gnutls_handshake(session);
+ }
+ while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0)
+ {
+ cry_error ("Handshake failed: %s", gnutls_strerror (ret));
+ gnutls_alert_send_appropriate(session, ret);
+ goto error;
+ }
+
+ /* Verify the client's certificate, if any. */
+ if (config->verify)
+ {
+ ret = gnutls_certificate_verify_peers2 (session, &status);
+ if (ret < 0)
+ cry_log ("Error getting certificate from client: %s",
+ gnutls_strerror (ret));
+
+ if (ret == 0 && status != 0)
+ switch (ret)
+ {
+ case GNUTLS_CERT_INVALID:
+ cry_log ("%s", "Client certificate not trusted or invalid");
+ break;
+ default:
+ cry_log ("%s", "Unknown error while getting the certificate");
+ break;
+ }
+
+ if (config->verify > 1 && (ret < 0 || status != 0))
+ {
+ ret = -1;
+ goto error;
+ }
+ }
+
+ /* Connect to the remote host */
+ sock = _crywrap_remote_connect (config->dest.addr,
+ htons (config->dest.port));
+
+ for (;;)
+ {
+ FD_ZERO (&fdset);
+ FD_SET (insock, &fdset);
+ FD_SET (sock, &fdset);
+
+ memset (buffer, 0, _CRYWRAP_MAXBUF + 1);
+
+ tls_pending = 0;
+
+ if (gnutls_record_check_pending(session) > 0)
+ tls_pending = 1;
+ else
+ {
+ select (sock + 1, &fdset, NULL, NULL, NULL);
+ if (FD_ISSET (insock, &fdset))
+ tls_pending = 1;
+ }
+ /* TLS client */
+ if (tls_pending != 0)
+ {
+ ret = gnutls_record_recv (session, buffer, _CRYWRAP_MAXBUF);
+ if (ret == 0)
+ {
+ cry_log ("%s", "Peer has closed the GNUTLS connection");
+ break;
+ }
+ else if (ret < 0)
+ {
+ cry_log ("Received corrupted data: %s.",
+ gnutls_strerror (ret));
+ break;
+ }
+ else
+ send (sock, buffer, ret, 0);
+ }
+
+ /* Remote server */
+ if (FD_ISSET (sock, &fdset))
+ {
+ ret = recv (sock, buffer, _CRYWRAP_MAXBUF, 0);
+ if (ret == 0)
+ {
+ cry_log ("%s", "Server has closed the connection");
+ break;
+ }
+ else if (ret < 0)
+ {
+ cry_log ("Received corrupted data: %s.", strerror (errno));
+ break;
+ }
+ else
+ {
+ int r, o = 0;
+
+ do
+ {
+ r = gnutls_record_send (session, &buffer[o], ret - o);
+ o += r;
+ } while (r > 0 && ret > o);
+
+ if (r < 0)
+ cry_log ("Received corrupted data: %s", gnutls_strerror (r));
+ }
+ }
+ }
+
+error:
+ gnutls_bye (session, GNUTLS_SHUT_WR);
+ gnutls_deinit (session);
+ close (insock);
+ close (outsock);
+
+ return (ret == 0) ? 0 : 1;
+}
+
+/** CryWrap entry point.
+ * This is the main entry point - controls the whole program and so
+ * on...
+ */
+int
+main (int argc, char **argv, char **envp)
+{
+ crywrap_config_t *config;
+ int server_socket;
+
+ openlog (__CRYWRAP__, LOG_PID, LOG_DAEMON);
+
+ if (gnutls_global_init () < 0)
+ {
+ cry_error ("%s", "Global TLS state initialisation failed.");
+ exit (1);
+ }
+ if (gnutls_certificate_allocate_credentials (&cred) < 0)
+ {
+ cry_error ("%s", "Couldn't allocate credentials.");
+ exit (1);
+ }
+
+ stringprep_locale_charset ();
+
+ config = _crywrap_config_parse (argc, argv);
+ set_program_name(__CRYWRAP__);
+
+ _crywrap_tls_init ();
+
+ if (config->inetd)
+ {
+ _crywrap_privs_drop (config);
+ exit (_crywrap_do_one (config, 0, 1));
+ }
+
+#if CRYWRAP_OPTION_FORK
+ if (!config->debug)
+ if (daemon (0, 0))
+ {
+ cry_error ("daemon: %s", strerror (errno));
+ exit (1);
+ }
+#endif
+
+ cry_log ("%s", "Crywrap starting...");
+
+ server_socket = _crywrap_listen (config);
+ if (server_socket < 0)
+ exit (1);
+
+ if (!config->debug) _crywrap_setup_pidfile (config);
+ _crywrap_privs_drop (config);
+
+ signal (SIGTERM, _crywrap_sighandler);
+ signal (SIGQUIT, _crywrap_sighandler);
+ signal (SIGSEGV, _crywrap_sighandler);
+ signal (SIGPIPE, SIG_IGN);
+ signal (SIGHUP, _crywrap_sighup_handler);
+
+ cry_log ("%s", "Accepting connections");
+
+ memset (crywrap_children, 0, sizeof (crywrap_children));
+ signal (SIGCHLD, _crywrap_sigchld_handler);
+
+ for (;;)
+ {
+ int csock;
+#if !BHC_OPTION_DEBUG
+ int child;
+#endif
+
+ if (sigchld)
+ _crywrap_reap_children ();
+
+ csock = accept (server_socket, NULL, NULL);
+ if (csock < 0)
+ continue;
+
+#if !BHC_OPTION_DEBUG
+ child = fork ();
+ switch (child)
+ {
+ case 0:
+ exit (_crywrap_do_one (config, csock, csock));
+ break;
+ case -1:
+ cry_error ("%s", "Forking error.");
+ exit (1);
+ break;
+ default:
+ crywrap_children[csock] = child;
+ break;
+ }
+#else
+ _crywrap_do_one (config, csock, csock);
+#endif
+ close(csock);
+ }
+
+ return 0;
+}
+
+static int system_log(const char* fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsyslog(LOG_NOTICE, fmt, args);
+ va_end (args);
+
+ return 0;
+}
+
+static int system_log_error(const char* fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsyslog(LOG_ERR, fmt, args);
+ va_end (args);
+
+ return 0;
+}
+
+static int debug_log(const char* fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vprintf(fmt, args);
+ puts("");
+ va_end (args);
+
+ return 0;
+}
+
+/** @} */
+
diff --git a/src/crywrap/crywrap.h b/src/crywrap/crywrap.h
new file mode 100644
index 0000000000..5cadd5dda9
--- /dev/null
+++ b/src/crywrap/crywrap.h
@@ -0,0 +1,105 @@
+/* -*- mode: c; c-file-style: "gnu" -*-
+ * crywrap.h -- Global definitions for CryWrap
+ * Copyright (C) 2003, 2004 Gergely Nagy <algernon@bonehunter.rulez.org>
+ *
+ * This file is part of CryWrap.
+ *
+ * CryWrap 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.
+ *
+ * CryWrap 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** @file crywrap.h
+ * Global variables and declarations for CryWrap.
+ *
+ * All of the global types, structures and whatnot are declared in
+ * this file. Not variables, though. Those are in crywrap.c.
+ */
+
+#ifndef _CRYWRAP_H
+#define _CRYWRAP_H 1 /**< crywrap.h multi-inclusion guard. */
+
+/** @defgroup defaults Built-in defaults.
+ * @{
+ */
+#define __CRYWRAP__ "crywrap" /**< Software name. */
+/** Software version.
+ */
+#define _CRYWRAP_VERSION "0.2." CRYWRAP_PATCHLEVEL
+/** Configuration directory.
+ */
+#define _CRYWRAP_CONFDIR SYSCONFDIR "/crywrap"
+#define _CRYWRAP_UID 65534 /**< Default UID to run as. */
+/** Default PID file.
+ */
+#define _CRYWRAP_PIDFILE "/var/run/crywrap.pid"
+/** Maximum number of clients supported.
+ */
+#define _CRYWRAP_MAXCONN 1024
+/** Maximum I/O buffer size.
+ */
+#define _CRYWRAP_MAXBUF 64 * 1024
+/** Default server certificate and key.
+ */
+#define _CRYWRAP_PEMFILE _CRYWRAP_CONFDIR "/server.pem"
+/** @} */
+
+/** Configuration structure.
+ * Most of the CryWrap configuration - those options that are settable
+ * via the command-line are stored in a variable of this type.
+ */
+typedef struct
+{
+ /** Properties of the listening socket.
+ */
+ struct
+ {
+ in_port_t port;
+ struct sockaddr_storage *addr;
+ } listen;
+
+ /** Properties of the destination socket.
+ */
+ struct
+ {
+ in_port_t port;
+ char *host;
+ struct sockaddr_storage *addr;
+ } dest;
+
+ const char *pidfile; /**< File to store our PID in. */
+ uid_t uid; /**< User ID to run as. */
+ int inetd; /**< InetD-mode toggle. */
+ int anon; /**< Anon-DH toggle. */
+ int verify; /**< Client certificate verify level. */
+ int debug;
+} crywrap_config_t;
+
+/** @defgroup options Options.
+ * These are the compile-time options.
+ * @{
+ */
+/** If this option is set, CryWrap will fork into the background.
+ */
+#ifndef CRYWRAP_OPTION_FORK
+#define CRYWRAP_OPTION_FORK 1
+#endif
+
+#if CRYWRAP_OPTION_NOFORK
+#undef CRYWRAP_OPTION_FORK
+#endif
+
+/** @} *//* End of the Options group */
+
+#endif /* !_CRYWRAP_H */
+
+/* arch-tag: ebfe1550-0fec-4c0d-8833-23e48292e75d */
diff --git a/src/crywrap/primes.h b/src/crywrap/primes.h
new file mode 100644
index 0000000000..50c331d39d
--- /dev/null
+++ b/src/crywrap/primes.h
@@ -0,0 +1,42 @@
+/* primes.h -- initial DH primes for CryWrap
+ *
+ * Copyright (C) 2003 Gergely Nagy <algernon@bonehunter.rulez.org>
+ * Copyright (C) 2011 Nikos Mavrogiannopoulos
+ *
+ * This file is part of CryWrap.
+ *
+ * CryWrap 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.
+ *
+ * CryWrap 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** @file primes.h
+ * Initial DH primes for CryWrap.
+ *
+ * In order to speed up the startup time, CryWrap does not generate a
+ * new DH prime upon every startup, but only when it receives a
+ * SIGHUP.
+ */
+
+#ifndef _CRYWRAP_PRIMES_H
+#define _CRYWRAP_PRIMES_H
+
+/** Initial DH primes, 1024 bits.
+ */
+static char _crywrap_prime_dh_1024[] = "-----BEGIN DH PARAMETERS-----\n"
+"MIGHAoGBAO6vCrmts43WnDP4CvqPxehgcmGHdf88C56iMUycJWV21nTfdJbqgdM4\n"
+"O0gT1pLG4ODV2OJQuYvkjklcHWCJ2tFdx9e0YVTWts6O9K1psV1JglWbKXvPGIXF\n"
+"KfVmZg5X7GjtvDwFcmzAL9TL9Jduqpr9UTj+g3ZDW5/GHS/A6wbjAgEC\n"
+"-----END DH PARAMETERS-----\n";
+
+#endif
+