updated
[crywrap.git] / src / crywrap.c
blob4d4a54bca78e7720d43f90b5029880c377a5f94e
1 /* -*- mode: c; c-file-style: "gnu" -*-
2 * crywrap.c -- CryWrap
3 * Copyright (C) 2003, 2004 Gergely Nagy <algernon@bonehunter.rulez.org>
4 * Copyright (C) 2011 Nikos Mavrogiannopoulos
6 * This file is part of CryWrap.
8 * CryWrap is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * CryWrap is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 * License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 /** @file crywrap.c
24 * CryWrap itself.
27 #include <system.h>
28 #include "compat/compat.h"
30 #ifdef HAVE_ARGP_H
31 #include <argp.h>
32 #endif
33 #include <arpa/inet.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <gnutls/gnutls.h>
37 #include <gnutls/x509.h>
38 #include <grp.h>
39 #include <idna.h>
40 #include <netdb.h>
41 #include <netinet/in.h>
42 #include <pwd.h>
43 #include <signal.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <stringprep.h>
48 #include <sys/select.h>
49 #include <sys/socket.h>
50 #include <sys/types.h>
51 #include <sys/wait.h>
52 #include <syslog.h>
53 #include <unistd.h>
55 #include "crywrap.h"
56 #include "primes.h"
58 static int bhc_log(const char* fmt, ...)
60 va_list args;
62 va_start (args, fmt);
63 vsyslog(LOG_NOTICE, fmt, args);
64 va_end (args);
66 return 0;
69 static int bhc_error(const char* fmt, ...)
71 va_list args;
73 va_start (args, fmt);
74 vsyslog(LOG_ERR, fmt, args);
75 va_end (args);
77 return 0;
80 static int debug_log(const char* fmt, ...)
82 va_list args;
84 va_start (args, fmt);
85 vprintf(fmt, args);
86 puts("");
87 va_end (args);
89 return 0;
92 typedef int (*cry_log_func)(const char *format, ...);
93 static cry_log_func cry_log = bhc_log;
94 static cry_log_func cry_error = bhc_error;
96 /** @defgroup globals Global variables.
97 * @{
99 /** Status flag to toggle on SIGCHLD.
101 static sig_atomic_t sigchld = 0;
102 /** An array of pids.
103 * This array holds the PIDs of all of our children, indexed by the
104 * socket the associated client connected to us.
106 static pid_t crywrap_children[_CRYWRAP_MAXCONN + 2];
107 static pid_t main_pid = -1; /**< Pid of the main process */
108 static char *pidfile = _CRYWRAP_PIDFILE; /**< File to log our PID
109 into. */
110 /** GNUTLS server credentials.
112 static gnutls_certificate_server_credentials cred;
113 static gnutls_dh_params dh_params; /**< GNUTLS DH parameters. */
114 static const int dh_bits = 1024; /**< GNUTLS DH bits. */
116 /** Bugreport address.
117 * Used by the argp suite.
119 const char *argp_program_bug_address = "<bugs-gnutls@gnu.org>";
120 /** Porgram version.
121 * Used by the argp suite.
123 const char *argp_program_version = __CRYWRAP__ " " _CRYWRAP_VERSION;
125 /** The options CryWrap takes.
126 * Used by the argp suite.
128 static const struct argp_option _crywrap_options[] = {
129 {NULL, 0, NULL, 0, "Mandatory options:", 1},
130 {"destination", 'd', "IP/PORT", 0, "IP and port to connect to", 1},
131 {NULL, 0, NULL, 0, "TLS certificates:", 2},
132 {"pem", 'p', "TYPE=PATH", 0, "Server key and certificate", 2},
133 {"anon", 'a', NULL, 0, "Enable Anon-DH (don't use a certificate)", 2},
134 {"verify", 'v', "LEVEL", OPTION_ARG_OPTIONAL,
135 "Verify clients certificate", 2},
136 {NULL, 0, NULL, 0, "Other options:", 3},
137 {"user", 'u', "UID", 0, "User ID to run as", 3},
138 {"pidfile", 'P', "PATH", 0, "File to log the PID into", 3},
139 {"inetd", 'i', NULL, 0, "Enable inetd mode", 3},
140 {"listen", 'l', "IP/PORT", 0, "IP and port to listen on", 3},
141 {"debug", 'D', NULL, 0, "Do not fork", 2},
142 {0, 0, 0, 0, NULL, 0}
145 static error_t _crywrap_config_parse_opt (int key, char *arg,
146 struct argp_state *state);
147 /** The main argp structure for Crywrap.
149 static const struct argp _crywrap_argp =
150 {_crywrap_options, _crywrap_config_parse_opt, 0,
151 __CRYWRAP__ " -- Security for the masses\v"
152 "The --destination option is mandatory, as is --listen if --inetd "
153 "was not used.",
154 NULL, NULL, NULL};
156 #ifndef __DOXYGEN__
157 enum
159 CRYWRAP_P_SUBOPT_CERT,
160 CRYWRAP_P_SUBOPT_KEY,
161 CRYWRAP_P_SUBOPT_END
163 #endif
165 /** Helper structure for parsing --pem subopts.
167 static char *_crywrap_p_subopts[] = {
168 [CRYWRAP_P_SUBOPT_CERT] = "cert",
169 [CRYWRAP_P_SUBOPT_KEY] = "key",
170 [CRYWRAP_P_SUBOPT_END] = NULL
173 /** Helper variable to set if a certificate was explictly set.
175 static int cert_seen = 0;
177 /** @} */
179 /* Forward declaration */
180 static int _crywrap_dh_params_generate (void);
182 /** @defgroup signal Signal handlers & co.
183 * @{
186 /** SIGCHLD handler
188 static void
189 _crywrap_sigchld_handler (int sig)
191 sigchld = 1;
192 signal (sig, _crywrap_sigchld_handler);
195 /** SIGHUP handler.
196 * Regenerates DH and RSA paramaters. Takes a bit long...
198 static void
199 _crywrap_sighup_handler (int sig)
201 _crywrap_dh_params_generate ();
203 gnutls_certificate_set_dh_params (cred, dh_params);
205 signal (sig, _crywrap_sighup_handler);
208 /** Generic signal handler.
209 * This one removes the #pidfile, if necessary.
211 static void
212 _crywrap_sighandler (int sig)
214 if (getpid () == main_pid)
216 cry_log ("Exiting on signal %d", sig);
217 if (pidfile && *pidfile)
218 unlink (pidfile);
219 closelog ();
220 exit (0);
223 /** @} */
225 /** @defgroup parsing Option parsing
226 * @{
229 /** Service resolver.
230 * Resolves a service - be it a name or a number.
232 * @param serv is the port to resolve.
234 * @returns The purt number, or -1 on error.
236 static int
237 _crywrap_port_get (const char *serv)
239 int port;
240 struct servent *se;
242 if (!serv)
243 return -1;
245 se = getservbyname (serv, "tcp");
246 if (!se)
247 port = atoi (serv);
248 else
249 port = ntohs (se->s_port);
251 return port;
254 /** Address resolver.
255 * Resolves an address - be it numeric or a hostname, IPv4 or IPv6.
257 * @param hostname is the host to resolve.
258 * @param addr is the structure to put the result into.
260 * @returns Zero on success, -1 on error.
262 static int
263 _crywrap_addr_get (const char *hostname, struct sockaddr_storage **addr)
265 struct addrinfo *res;
266 struct addrinfo hints;
267 ssize_t len;
268 char *lz = NULL;
270 if (idna_to_ascii_lz (hostname, &lz, 0) != IDNA_SUCCESS)
271 return -1;
273 memset (&hints, 0, sizeof (hints));
274 hints.ai_family = PF_UNSPEC;
275 hints.ai_socktype = SOCK_STREAM;
276 hints.ai_protocol = IPPROTO_IP;
277 *addr = bhc_calloc (1, sizeof (struct sockaddr_storage));
279 if (getaddrinfo (lz, NULL, &hints, &res) != 0)
281 free (lz);
282 return -1;
285 free (lz);
287 switch (res->ai_addr->sa_family)
289 case AF_INET:
290 len = sizeof (struct sockaddr_in);
291 break;
292 case AF_INET6:
293 len = sizeof (struct sockaddr_in6);
294 break;
295 default:
296 freeaddrinfo (res);
297 return -1;
300 if (len < (ssize_t)res->ai_addrlen)
302 freeaddrinfo (res);
303 return -1;
306 memcpy (*addr, res->ai_addr, res->ai_addrlen);
307 freeaddrinfo (res);
309 return 0;
312 /** Parse a HOST/IP pair.
313 * Splits up a given HOST/IP pair, and converts them into structures
314 * directly usable by libc routines.
316 * @param ip is the HOST/IP pair to parse.
317 * @param port is a pointer to an integer where the port number should
318 * go.
319 * @param addr is the destination of the resolved and parsed IP.
321 * @returns Zero on success, -1 on error.
323 static int
324 _crywrap_parse_ip (const char *ip, in_port_t *port,
325 struct sockaddr_storage **addr, char **host)
327 char *s_ip;
328 char *tmp;
330 tmp = strchr (ip, '/');
332 if (!tmp)
333 return -1;
335 if (tmp == ip)
337 s_ip = bhc_strdup ("0.0.0.0");
338 *port = (in_port_t)_crywrap_port_get (&ip[1]);
340 else
342 *port = (in_port_t)_crywrap_port_get (&tmp[1]);
343 s_ip = bhc_strndup (ip, tmp - ip);
346 if (!*port)
347 return -1;
349 if (host)
350 *host = strdup (s_ip);
352 return _crywrap_addr_get (s_ip, addr);
355 /** Argument parsing routine.
356 * Used by the argp suite.
358 static error_t
359 _crywrap_config_parse_opt (int key, char *arg, struct argp_state *state)
361 crywrap_config_t *cfg = (crywrap_config_t *)state->input;
362 char *pem_cert, *pem_key, *subopts, *value;
364 switch (key)
366 case 'D':
367 cfg->debug = 1;
368 cry_log = debug_log;
369 cry_error = debug_log;
370 break;
371 case 'd':
372 if (_crywrap_parse_ip (arg, &cfg->dest.port, &cfg->dest.addr,
373 &cfg->dest.host) < 0)
374 argp_error (state, "Could not resolve address: `%s'", arg);
375 break;
376 case 'l':
377 if (_crywrap_parse_ip (arg, &cfg->listen.port,
378 &cfg->listen.addr, NULL) < 0)
379 argp_error (state, "Could not resolve address: `%s'", arg);
380 break;
381 case 'u':
382 cfg->uid = atoi (arg);
383 break;
384 case 'P':
385 if (arg && *arg)
386 cfg->pidfile = bhc_strdup (arg);
387 else
388 cfg->pidfile = NULL;
389 break;
390 case 'p':
391 subopts = optarg;
392 pem_cert = NULL;
393 pem_key = NULL;
394 while (*subopts != '\0')
395 switch (bhc_getsubopt (&subopts, _crywrap_p_subopts, &value))
397 case CRYWRAP_P_SUBOPT_CERT:
398 pem_cert = bhc_strdup (value);
399 break;
400 case CRYWRAP_P_SUBOPT_KEY:
401 pem_key = bhc_strdup (value);
402 break;
403 default:
404 pem_cert = bhc_strdup (value);
405 break;
407 if (!pem_key)
408 pem_key = bhc_strdup (pem_cert);
409 if (!pem_cert)
410 pem_cert = bhc_strdup (pem_key);
411 if (gnutls_certificate_set_x509_key_file (cred, pem_cert, pem_key,
412 GNUTLS_X509_FMT_PEM) < 0)
413 argp_error (state, "error reading X.509 key or certificate file.");
414 cert_seen = 1;
415 break;
416 case 'i':
417 cfg->inetd = 1;
418 break;
419 case 'a':
420 cfg->anon = 1;
421 cfg->verify = 0;
422 break;
423 case 'v':
424 cfg->verify = (arg) ? atoi (arg) : 1;
425 break;
426 case ARGP_KEY_END:
427 if (!cfg->inetd)
429 if (!cfg->listen.addr || !cfg->dest.addr)
430 argp_error
431 (state,
432 "a listening and a destination address must be set!");
434 else
435 if (!cfg->dest.addr)
436 argp_error (state, "a destination address must be set!");
437 if (cert_seen)
438 break;
439 if (cfg->anon)
440 break;
441 if (gnutls_certificate_set_x509_key_file (cred, _CRYWRAP_PEMFILE,
442 _CRYWRAP_PEMFILE,
443 GNUTLS_X509_FMT_PEM) < 0)
444 argp_error (state, "error reading X.509 key or certificate file.");
445 break;
446 default:
447 return ARGP_ERR_UNKNOWN;
449 return 0;
452 /** Configuration parsing.
453 * Sets up the default values, and parses the command-line options
454 * using argp.
456 * @note Does not return if an error occurred.
458 static crywrap_config_t *
459 _crywrap_config_parse (int argc, char **argv)
461 crywrap_config_t *config =
462 (crywrap_config_t *)bhc_malloc (sizeof (crywrap_config_t));
464 config->listen.port = 0;
465 config->listen.addr = NULL;
466 config->dest.port = 0;
467 config->dest.addr = NULL;
469 config->uid = _CRYWRAP_UID;
470 config->pidfile = _CRYWRAP_PIDFILE;
471 config->inetd = 0;
472 config->anon = 0;
473 config->verify = 1;
475 argp_parse (&_crywrap_argp, argc, argv, 0, 0, config);
477 return config;
479 /** @} */
481 /** @defgroup tls Lower-level TLS routines.
482 * @{
485 /** Create a GNUTLS session.
486 * Initialises the cyphers and the session database for a new TLS
487 * session.
489 * @returns The newly created TLS session.
491 static gnutls_session
492 _crywrap_tls_session_create (const crywrap_config_t *config)
494 gnutls_session session;
496 gnutls_init (&session, GNUTLS_SERVER);
499 if (config->anon) {
500 gnutls_priority_set_direct(session, "NORMAL:+ANON-ECDH:+ANON-DH", NULL);
501 gnutls_credentials_set (session, GNUTLS_CRD_ANON, cred);
502 } else {
503 gnutls_priority_set_direct(session, "NORMAL", NULL);
504 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, cred);
507 gnutls_dh_set_prime_bits (session, dh_bits);
509 gnutls_handshake_set_private_extensions (session, 1);
511 if (config->verify)
512 gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
514 return session;
517 /** (Re)Initialise Diffie Hellman parameters.
518 * @returns Zero.
520 static int
521 _crywrap_dh_params_generate (void)
523 if (gnutls_dh_params_init (&dh_params) < 0)
525 cry_error ("%s", "Error in dh parameter initialisation.");
526 bhc_exit (3);
529 if (gnutls_dh_params_generate2 (dh_params, dh_bits) < 0)
531 cry_error ("%s", "Error in prime generation.");
532 bhc_exit (3);
535 gnutls_certificate_set_dh_params (cred, dh_params);
537 return 0;
540 /** Generate initial DH and RSA params.
541 * Loads the pre-generated DH primes.
543 static void
544 _crywrap_tls_init (void)
546 gnutls_datum dh = { _crywrap_prime_dh_1024, sizeof(_crywrap_prime_dh_1024) };
548 gnutls_dh_params_init (&dh_params);
549 gnutls_dh_params_import_pkcs3 (dh_params, &dh, GNUTLS_X509_FMT_PEM);
551 gnutls_certificate_set_dh_params (cred, dh_params);
553 /** @} */
555 /** @defgroup networking Networking
556 * @{
559 /** Bind to an address.
560 * This one binds to an address, handles errors and anything that may
561 * arise.
563 * @param ai is the address information.
564 * @param listen_port is the port to bind to, and listen on.
566 * @returns The bound filedescriptor, or -1 on error.
568 static int
569 _crywrap_bind (const struct addrinfo *ai, int listen_port)
571 int ret;
572 const int one = 1;
573 int listenfd;
574 char sock_name[NI_MAXHOST];
576 listenfd = socket (ai->ai_family, SOCK_STREAM, IPPROTO_IP);
577 if (listenfd == -1)
579 cry_error ("socket: %s", strerror (errno));
580 return -1;
583 memset (sock_name, 0, sizeof (sock_name));
584 getnameinfo ((struct sockaddr *)ai->ai_addr, ai->ai_addrlen, sock_name,
585 sizeof (sock_name), NULL, 0, NI_NUMERICHOST);
587 switch (ai->ai_family)
589 case AF_INET6:
590 ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_port = listen_port;
591 break;
592 case AF_INET:
593 ((struct sockaddr_in *)(ai->ai_addr))->sin_port = listen_port;
594 break;
597 ret = setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR,
598 &one, sizeof (one));
599 if (ret != 0)
601 cry_error ("setsockopt: %s (%s)", strerror (errno), sock_name);
602 return -1;
605 ret = bind (listenfd, ai->ai_addr, ai->ai_addrlen);
606 if (ret != 0)
608 cry_error ("bind to %s failed: %s", sock_name, strerror (errno));
609 return -1;
612 if (listen (listenfd, _CRYWRAP_MAXCONN) != 0)
614 cry_error ("listen on %s failed: %s", sock_name, strerror (errno));
615 return -1;
618 cry_log ("Socket bound to port %d on %s.", ntohs (listen_port), sock_name);
620 return listenfd;
623 /** Set up a listening socket.
624 * Sets up a listening socket on all the required addresses.
626 * @param config holds the CryWrap configuration, from where the
627 * listen address and port will be extracted.
629 * @returns The listening FD on success, -1 on error.
631 static int
632 _crywrap_listen (const crywrap_config_t *config)
634 struct addrinfo *cur;
635 int ret;
637 cur = bhc_calloc (1, sizeof (struct addrinfo));
638 cur->ai_family = config->listen.addr->ss_family;
640 switch (cur->ai_family)
642 case AF_INET6:
643 cur->ai_addrlen = sizeof (struct sockaddr_in6);
644 break;
645 case AF_INET:
646 cur->ai_addrlen = sizeof (struct sockaddr_in);
647 break;
650 cur->ai_addr = bhc_malloc (cur->ai_addrlen);
651 memcpy (cur->ai_addr, config->listen.addr, cur->ai_addrlen);
653 ret = _crywrap_bind (cur, htons (config->listen.port));
654 free (cur->ai_addr);
655 free (cur);
657 return ret;
660 /** Connect to a remote server.
661 * Estabilishes a connection to a remote server, and handles all
662 * errors and anything that may arise during this process.
664 * @param addr is the address of the remote server.
665 * @param port is the port to connect to.
667 * @returns the connected socket on success, otherwise it exits.
669 static int
670 _crywrap_remote_connect (const struct sockaddr_storage *addr, int port)
672 struct addrinfo *cur;
673 int sock;
675 cur = bhc_calloc (1, sizeof (struct addrinfo));
676 cur->ai_family = addr->ss_family;
678 switch (cur->ai_family)
680 case AF_INET6:
681 cur->ai_addrlen = sizeof (struct sockaddr_in6);
682 break;
683 case AF_INET:
684 cur->ai_addrlen = sizeof (struct sockaddr_in);
685 break;
688 cur->ai_addr = bhc_malloc (cur->ai_addrlen);
689 memcpy (cur->ai_addr, addr, cur->ai_addrlen);
691 switch (cur->ai_family)
693 case AF_INET6:
694 ((struct sockaddr_in6 *)(cur->ai_addr))->sin6_port = port;
695 break;
696 case AF_INET:
697 ((struct sockaddr_in *)(cur->ai_addr))->sin_port = port;
698 break;
701 sock = socket (cur->ai_family, SOCK_STREAM, IPPROTO_IP);
702 if (sock < 0)
704 cry_error ("socket(): %s", strerror (errno));
705 bhc_exit (1);
708 if (connect (sock, cur->ai_addr, cur->ai_addrlen) < 0)
710 cry_error ("connect(): %s", strerror (errno));
711 bhc_exit (1);
714 free (cur->ai_addr);
715 free (cur);
717 return sock;
720 /** @} */
722 /** @defgroup crywrap Main CryWrap code.
723 * @{
726 /** Drop privileges.
727 * Drop privileges, if running as root.
728 * Upon failure, it will make CryWrap exit.
730 static void
731 _crywrap_privs_drop (const crywrap_config_t *config)
733 struct passwd *pwd;
735 if (getuid () != 0)
737 cry_log ("%s", "Not running as root, not dropping privileges.");
738 return;
741 if ((pwd = getpwuid (config->uid)) == NULL)
743 cry_error ("getpwuid(): %s", strerror (errno));
744 bhc_exit (1);
747 if (initgroups (pwd->pw_name, pwd->pw_gid) == -1)
749 cry_error ("initgroups(): %s", strerror (errno));
750 bhc_exit (1);
753 if (setgid (pwd->pw_gid) == -1)
755 cry_error ("setgid(): %s", strerror (errno));
756 bhc_exit (1);
759 if (setuid (config->uid))
761 cry_error ("setuid(): %s", strerror (errno));
762 bhc_exit (1);
766 /** Set up the PID file.
767 * Checks if a #pidfile already exists, and create one - containing the
768 * current PID - if one does not.
770 * @note Exits upon error.
772 static void
773 _crywrap_setup_pidfile (const crywrap_config_t *config)
775 char mypid[128];
776 int pidfilefd;
778 if (!config->pidfile || !*(config->pidfile))
779 return;
781 if (!access (config->pidfile, F_OK))
783 cry_error ("Pidfile (%s) already exists. Exiting.", config->pidfile);
784 exit (1);
786 if ((pidfilefd = open (config->pidfile,
787 O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1)
789 cry_error ("Cannot create pidfile (%s): %s.\n", config->pidfile,
790 strerror (errno));
791 exit (1);
793 fchown (pidfilefd, config->uid, (gid_t)-1);
795 main_pid = getpid ();
796 snprintf (mypid, sizeof (mypid), "%d\n", main_pid);
797 write (pidfilefd, mypid, strlen (mypid));
798 close (pidfilefd);
799 pidfile = config->pidfile;
802 /** Child cleanup routine.
803 * Called after a SIGCHLD is received. Walks through #crywrap_children
804 * and closes the socket of the one that exited.
806 static void
807 _crywrap_reap_children (void)
809 pid_t child;
810 int status, i;
812 while ((child = waitpid (-1, &status, WNOHANG)) > (pid_t) 0)
814 for (i = 0; i < _CRYWRAP_MAXCONN; i++)
816 if (!crywrap_children[i])
817 continue;
818 if (child == crywrap_children[i])
820 shutdown (i, SHUT_RDWR);
821 close (i);
822 crywrap_children[i] = 0;
826 sigchld = 0;
829 /** Handles one client.
830 * This one connects to the remote server, and proxies every traffic
831 * between our client and the server.
833 * @param config is the main CryWrap configuration structure.
834 * @param insock is the socket through which the client sends input.
835 * @param outsock is the socket through which we send output.
837 * @note Exits on error.
839 static int
840 _crywrap_do_one (const crywrap_config_t *config, int insock, int outsock)
842 int sock, ret;
843 gnutls_session session;
844 char buffer[_CRYWRAP_MAXBUF + 2];
845 fd_set fdset;
846 unsigned int status = 0;
847 struct sockaddr_storage faddr;
848 socklen_t socklen = sizeof (struct sockaddr_storage);
849 char peer_name[NI_MAXHOST];
851 /* Log the connection */
852 if (getpeername (insock, (struct sockaddr *)&faddr, &socklen) != 0)
853 cry_error ("getpeername(): %s", strerror (errno));
854 else
856 getnameinfo ((struct sockaddr *)&faddr,
857 sizeof (struct sockaddr_storage), peer_name,
858 sizeof (peer_name), NULL, 0, NI_NUMERICHOST);
859 cry_log ("Accepted connection from %s on %d to %s/%d",
860 peer_name, insock, config->dest.host,
861 config->dest.port);
864 /* Do the handshake with our peer */
865 session = _crywrap_tls_session_create (config);
866 gnutls_transport_set_ptr2 (session,
867 (gnutls_transport_ptr_t)insock,
868 (gnutls_transport_ptr_t)outsock);
872 ret = gnutls_handshake(session);
874 while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
876 if (ret < 0)
878 cry_error ("Handshake failed: %s", gnutls_strerror (ret));
879 gnutls_alert_send_appropriate(session, ret);
880 goto error;
883 /* Verify the client's certificate, if any. */
884 if (config->verify)
886 ret = gnutls_certificate_verify_peers2 (session, &status);
887 if (ret < 0)
888 cry_log ("Error getting certificate from client: %s",
889 gnutls_strerror (ret));
891 if (ret == 0 && status != 0)
892 switch (ret)
894 case GNUTLS_CERT_INVALID:
895 cry_log ("%s", "Client certificate not trusted or invalid");
896 break;
897 default:
898 cry_log ("%s", "Unknown error while getting the certificate");
899 break;
902 if (config->verify > 1 && (ret < 0 || status != 0))
904 ret = -1;
905 goto error;
909 /* Connect to the remote host */
910 sock = _crywrap_remote_connect (config->dest.addr,
911 htons (config->dest.port));
913 for (;;)
915 FD_ZERO (&fdset);
916 FD_SET (insock, &fdset);
917 FD_SET (sock, &fdset);
919 bzero (buffer, _CRYWRAP_MAXBUF + 1);
920 select (sock + 1, &fdset, NULL, NULL, NULL);
922 /* TLS client */
923 if (FD_ISSET (insock, &fdset))
925 ret = gnutls_record_recv (session, buffer, _CRYWRAP_MAXBUF);
926 if (ret == 0)
928 cry_log ("%s", "Peer has closed the GNUTLS connection");
929 break;
931 else if (ret < 0)
933 cry_log ("Received corrupted data: %s.",
934 gnutls_strerror (ret));
935 break;
937 else
938 send (sock, buffer, ret, 0);
941 /* Remote server */
942 if (FD_ISSET (sock, &fdset))
944 ret = recv (sock, buffer, _CRYWRAP_MAXBUF, 0);
945 if (ret == 0)
947 cry_log ("%s", "Server has closed the connection");
948 break;
950 else if (ret < 0)
952 cry_log ("Received corrupted data: %s.", strerror (errno));
953 break;
955 else
957 int r, o = 0;
961 r = gnutls_record_send (session, &buffer[o], ret - o);
962 o += r;
963 } while (r > 0 && ret > o);
965 if (r < 0)
966 cry_log ("Received corrupted data: %s", gnutls_strerror (r));
971 error:
972 gnutls_bye (session, GNUTLS_SHUT_WR);
973 gnutls_deinit (session);
974 close (insock);
975 close (outsock);
977 return (ret == 0) ? 0 : 1;
980 /** CryWrap entry point.
981 * This is the main entry point - controls the whole program and so
982 * on...
985 main (int argc, char **argv, char **envp)
987 crywrap_config_t *config;
988 int server_socket;
990 openlog (__CRYWRAP__, LOG_PID, LOG_DAEMON);
992 if (gnutls_global_init () < 0)
994 cry_error ("%s", "Global TLS state initialisation failed.");
995 bhc_exit (1);
997 if (gnutls_certificate_allocate_credentials (&cred) < 0)
999 cry_error ("%s", "Couldn't allocate credentials.");
1000 bhc_exit (1);
1003 stringprep_locale_charset ();
1005 bhc_setproctitle_init (argc, argv, envp);
1006 config = _crywrap_config_parse (argc, argv);
1007 bhc_setproctitle (__CRYWRAP__);
1009 _crywrap_tls_init ();
1011 if (config->inetd)
1013 _crywrap_privs_drop (config);
1014 exit (_crywrap_do_one (config, 0, 1));
1017 #if CRYWRAP_OPTION_FORK
1018 if (!config->debug)
1019 if (daemon (0, 0))
1021 cry_error ("daemon: %s", strerror (errno));
1022 exit (1);
1024 #endif
1026 cry_log ("%s", "Crywrap starting...");
1028 server_socket = _crywrap_listen (config);
1029 if (server_socket < 0)
1030 exit (1);
1032 if (!config->debug) _crywrap_setup_pidfile (config);
1033 _crywrap_privs_drop (config);
1035 signal (SIGTERM, _crywrap_sighandler);
1036 signal (SIGQUIT, _crywrap_sighandler);
1037 signal (SIGSEGV, _crywrap_sighandler);
1038 signal (SIGPIPE, SIG_IGN);
1039 signal (SIGHUP, _crywrap_sighup_handler);
1041 cry_log ("%s", "Accepting connections");
1043 memset (crywrap_children, 0, sizeof (crywrap_children));
1044 signal (SIGCHLD, _crywrap_sigchld_handler);
1046 for (;;)
1048 int csock;
1049 #if !BHC_OPTION_DEBUG
1050 int child;
1051 #endif
1053 if (sigchld)
1054 _crywrap_reap_children ();
1056 csock = accept (server_socket, NULL, NULL);
1057 if (csock < 0)
1058 continue;
1060 #if !BHC_OPTION_DEBUG
1061 child = fork ();
1062 switch (child)
1064 case 0:
1065 exit (_crywrap_do_one (config, csock, csock));
1066 break;
1067 case -1:
1068 cry_error ("%s", "Forking error.");
1069 bhc_exit (1);
1070 break;
1071 default:
1072 crywrap_children[csock] = child;
1073 break;
1075 #else
1076 _crywrap_do_one (config, csock, csock);
1077 #endif
1078 close(csock);
1081 return 0;
1084 /** @} */
1086 /** arch-tag: 3d45d946-5d09-493d-ae6e-26effb71eb6b */