File: | src/crywrap/crywrap.c |
Location: | line 193, column 11 |
Description: | Although the value stored to 'child' is used in the enclosing expression, the value is never actually read from 'child' |
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 |
5 | * |
6 | * This file is part of CryWrap. |
7 | * |
8 | * CryWrap is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation, either version 3 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * CryWrap is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | /** @file crywrap.c |
23 | * CryWrap itself. |
24 | */ |
25 | |
26 | #include <config.h> |
27 | |
28 | #ifdef HAVE_ARGP_H |
29 | #include <argp.h> |
30 | #endif |
31 | #include <arpa/inet.h> |
32 | #include <errno(*__errno_location ()).h> |
33 | #include <fcntl.h> |
34 | #include <gnutls/gnutls.h> |
35 | #include <gnutls/x509.h> |
36 | #include <grp.h> |
37 | #include <idna.h> |
38 | #include <netdb.h> |
39 | #include <netinet/in.h> |
40 | #include <pwd.h> |
41 | #include <signal.h> |
42 | #include <stdio.h> |
43 | #include <stdlib.h> |
44 | #include <string.h> |
45 | #include <stringprep.h> |
46 | #include <sys/select.h> |
47 | #include <sys/socket.h> |
48 | #include <sys/types.h> |
49 | #include <sys/wait.h> |
50 | #include <syslog.h> |
51 | #include <stdarg.h> |
52 | #include <unistd.h> |
53 | |
54 | /* Gnulib portability files. */ |
55 | #include "progname.h" |
56 | #include "argp.h" |
57 | #include <read-file.h> |
58 | |
59 | #include "crywrap.h" |
60 | #include "primes.h" |
61 | |
62 | static int system_log(const char* fmt, ...) |
63 | #ifdef __GNUC__4 |
64 | __attribute__ ((format (printf, 1, 2))) |
65 | #endif |
66 | ; |
67 | |
68 | static int system_log_error(const char* fmt, ...) |
69 | #ifdef __GNUC__4 |
70 | __attribute__ ((format (printf, 1, 2))) |
71 | #endif |
72 | ; |
73 | |
74 | static int debug_log(const char* fmt, ...) |
75 | #ifdef __GNUC__4 |
76 | __attribute__ ((format (printf, 1, 2))) |
77 | #endif |
78 | ; |
79 | |
80 | typedef int (*cry_log_func)(const char *format, ...) |
81 | #ifdef __GNUC__4 |
82 | __attribute__ ((format (printf, 1, 2))) |
83 | #endif |
84 | ; |
85 | |
86 | static cry_log_func cry_log = system_log; |
87 | static cry_log_func cry_error = system_log_error; |
88 | |
89 | static void |
90 | tls_audit_log_func (gnutls_session_t session, const char *str) |
91 | { |
92 | char peer_name[NI_MAXHOST1025] = "Unknown"; |
93 | gnutls_transport_ptr_t r, s; |
94 | struct sockaddr_storage faddr; |
95 | socklen_t socklen = sizeof (struct sockaddr_storage); |
96 | |
97 | if (session != NULL((void*)0)) |
98 | { |
99 | gnutls_transport_get_ptr2(session, &r, &s); |
100 | |
101 | /* Log the connection */ |
102 | if (getpeername ((int)(long)r, (struct sockaddr *)&faddr, &socklen) != 0) |
103 | cry_error ("getpeername(): %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
104 | |
105 | cry_log ("Peer %s: %s", peer_name, str); |
106 | } |
107 | else |
108 | cry_log ("%s", str); |
109 | |
110 | } |
111 | |
112 | /** @defgroup globals Global variables. |
113 | * @{ |
114 | */ |
115 | |
116 | /** An array of pids. |
117 | * This array holds the PIDs of all of our children, indexed by the |
118 | * socket the associated client connected to us. |
119 | */ |
120 | static pid_t main_pid = -1; /**< Pid of the main process */ |
121 | static const char *pidfile = _CRYWRAP_PIDFILE"/var/run/crywrap.pid"; /**< File to log our PID |
122 | into. */ |
123 | |
124 | /** GNUTLS server credentials. |
125 | */ |
126 | static gnutls_certificate_server_credentials cred; |
127 | static gnutls_dh_paramsgnutls_dh_params_t dh_params; /**< GNUTLS DH parameters. */ |
128 | static gnutls_datumgnutls_datum_t dh_file = { _crywrap_prime_dh_1024, sizeof(_crywrap_prime_dh_1024) }; /**< Diffie Hellman parameters */ |
129 | |
130 | /** Bugreport address. |
131 | * Used by the argp suite. |
132 | */ |
133 | const char *argp_program_bug_address = "<bugs-gnutls@gnu.org>"; |
134 | /** Porgram version. |
135 | * Used by the argp suite. |
136 | */ |
137 | const char *argp_program_version = __CRYWRAP__"crywrap" " " _CRYWRAP_VERSION"0.2." "3"; |
138 | |
139 | /* The certificate and key files */ |
140 | static char *pem_cert = NULL((void*)0); |
141 | static char *pem_key = NULL((void*)0); |
142 | |
143 | |
144 | /** The options CryWrap takes. |
145 | * Used by the argp suite. |
146 | */ |
147 | static const struct argp_option _crywrap_options[] = { |
148 | {NULL((void*)0), 0, NULL((void*)0), 0, "Mandatory options:", 1}, |
149 | {"destination", 'd', "IP/PORT", 0, "IP and port to connect to", 1}, |
150 | {"listen", 'l', "IP/PORT", 0, "IP and port to listen on", 1}, |
151 | {NULL((void*)0), 0, NULL((void*)0), 0, "TLS certificates:", 2}, |
152 | {"key", 'k', "FILE", 0, "Server key", 2}, |
153 | {"cert", 'c', "FILE", 0, "Server certificate", 2}, |
154 | {"ca", 'z', "FILE", 0, "CA certificate", 2}, |
155 | {"anon", 'a', NULL((void*)0), 0, "Enable anonymous authentication (no certificates)", 2}, |
156 | {"verify", 'v', "LEVEL", OPTION_ARG_OPTIONAL0x1, |
157 | "Verify clients certificate (1: verify if exists, 2: require)", 2}, |
158 | {NULL((void*)0), 0, NULL((void*)0), 0, "Other options:", 3}, |
159 | {"dhparams", 'r', "FILE", 0, "Diffie Hellman (PKCS #3) parameters file", 3}, |
160 | {"user", 'u', "UID", 0, "User ID to run as", 3}, |
161 | {"pidfile", 'P', "PATH", 0, "File to log the PID into", 3}, |
162 | {"priority", 'p', "STRING", 0, "GnuTLS ciphersuite priority string", 3}, |
163 | {"inetd", 'i', NULL((void*)0), 0, "Enable inetd mode", 3}, |
164 | {"debug", 'D', NULL((void*)0), 0, "Run the server into foreground", 3}, |
165 | {0, 0, 0, 0, NULL((void*)0), 0} |
166 | }; |
167 | |
168 | static error_t _crywrap_config_parse_opt (int key, char *arg, |
169 | struct argp_state *state); |
170 | /** The main argp structure for Crywrap. |
171 | */ |
172 | static const struct argp _crywrap_argp = |
173 | {_crywrap_options, _crywrap_config_parse_opt, 0, |
174 | __CRYWRAP__"crywrap" " -- Security for the masses\v" |
175 | "The --destination option is mandatory, as is --listen if --inetd " |
176 | "was not used.", |
177 | NULL((void*)0), NULL((void*)0), NULL((void*)0)}; |
178 | |
179 | /** @} */ |
180 | |
181 | /** @defgroup signal Signal handlers & co. |
182 | * @{ |
183 | */ |
184 | |
185 | /** SIGCHLD handler |
186 | */ |
187 | static void |
188 | _crywrap_sigchld_handler (int sig) |
189 | { |
190 | pid_t child; |
191 | int status; |
192 | |
193 | while ((child = waitpid (-1, &status, WNOHANG1)) > (pid_t) 0) |
Although the value stored to 'child' is used in the enclosing expression, the value is never actually read from 'child' | |
194 | signal (sig, _crywrap_sigchld_handler); |
195 | } |
196 | |
197 | /* Helper functions to load a certificate and key |
198 | * files into memory. |
199 | */ |
200 | static gnutls_datum_t |
201 | load_file (const char *file) |
202 | { |
203 | gnutls_datum_t loaded_file = { NULL((void*)0), 0 }; |
204 | size_t length; |
205 | |
206 | loaded_file.data = read_binary_file_gnutls_read_binary_file (file, &length); |
207 | if (loaded_file.data) |
208 | loaded_file.size = (unsigned int) length; |
209 | |
210 | return loaded_file; |
211 | } |
212 | |
213 | /** Generic signal handler. |
214 | * This one removes the #pidfile, if necessary. |
215 | */ |
216 | static void |
217 | _crywrap_sighandler (int sig) |
218 | { |
219 | if (getpid () == main_pid) |
220 | { |
221 | cry_log ("Exiting on signal %d", sig); |
222 | if (pidfile && *pidfile) |
223 | unlink (pidfile); |
224 | closelog (); |
225 | exit (0); |
226 | } |
227 | } |
228 | /** @} */ |
229 | |
230 | /** @defgroup parsing Option parsing |
231 | * @{ |
232 | */ |
233 | |
234 | /** Service resolver. |
235 | * Resolves a service - be it a name or a number. |
236 | * |
237 | * @param serv is the port to resolve. |
238 | * |
239 | * @returns The purt number, or -1 on error. |
240 | */ |
241 | static int |
242 | _crywrap_port_get (const char *serv) |
243 | { |
244 | int port; |
245 | struct servent *se; |
246 | |
247 | if (!serv) |
248 | return -1; |
249 | |
250 | se = getservbyname (serv, "tcp"); |
251 | if (!se) |
252 | port = atoi (serv); |
253 | else |
254 | port = ntohs (se->s_port); |
255 | |
256 | return port; |
257 | } |
258 | |
259 | /** Address resolver. |
260 | * Resolves an address - be it numeric or a hostname, IPv4 or IPv6. |
261 | * |
262 | * @param hostname is the host to resolve. |
263 | * @param addr is the structure to put the result into. |
264 | * |
265 | * @returns Zero on success, -1 on error. |
266 | */ |
267 | static int |
268 | _crywrap_addr_get (const char *hostname, struct sockaddr_storage **addr) |
269 | { |
270 | struct addrinfo *res; |
271 | struct addrinfo hints; |
272 | ssize_t len; |
273 | char *lz = NULL((void*)0); |
274 | |
275 | if (idna_to_ascii_lz (hostname, &lz, 0) != IDNA_SUCCESS) |
276 | return -1; |
277 | |
278 | memset (&hints, 0, sizeof (hints)); |
279 | hints.ai_family = PF_UNSPEC0; |
280 | hints.ai_socktype = SOCK_STREAMSOCK_STREAM; |
281 | hints.ai_protocol = IPPROTO_IPIPPROTO_IP; |
282 | *addr = calloc (1, sizeof (struct sockaddr_storage)); |
283 | if (*addr == NULL((void*)0)) |
284 | { |
285 | free(lz); |
286 | return -1; |
287 | } |
288 | |
289 | if (getaddrinfo (lz, NULL((void*)0), &hints, &res) != 0) |
290 | { |
291 | free (lz); |
292 | return -1; |
293 | } |
294 | |
295 | free (lz); |
296 | |
297 | switch (res->ai_addr->sa_family) |
298 | { |
299 | case AF_INET2: |
300 | len = sizeof (struct sockaddr_in); |
301 | break; |
302 | case AF_INET610: |
303 | len = sizeof (struct sockaddr_in6); |
304 | break; |
305 | default: |
306 | freeaddrinfo (res); |
307 | return -1; |
308 | } |
309 | |
310 | if (len < (ssize_t)res->ai_addrlen) |
311 | { |
312 | freeaddrinfo (res); |
313 | return -1; |
314 | } |
315 | |
316 | memcpy (*addr, res->ai_addr, res->ai_addrlen); |
317 | freeaddrinfo (res); |
318 | |
319 | return 0; |
320 | } |
321 | |
322 | /** Parse a HOST/IP pair. |
323 | * Splits up a given HOST/IP pair, and converts them into structures |
324 | * directly usable by libc routines. |
325 | * |
326 | * @param ip is the HOST/IP pair to parse. |
327 | * @param port is a pointer to an integer where the port number should |
328 | * go. |
329 | * @param addr is the destination of the resolved and parsed IP. |
330 | * |
331 | * @returns Zero on success, -1 on error. |
332 | */ |
333 | static int |
334 | _crywrap_parse_ip (const char *ip, in_port_t *port, |
335 | struct sockaddr_storage **addr, char **host) |
336 | { |
337 | char *s_ip; |
338 | char *tmp; |
339 | |
340 | tmp = strchr (ip, '/'); |
341 | |
342 | if (!tmp) |
343 | return -1; |
344 | |
345 | if (tmp == ip) |
346 | { |
347 | s_ip = strdup ("0.0.0.0"); |
348 | *port = (in_port_t)_crywrap_port_get (&ip[1]); |
349 | } |
350 | else |
351 | { |
352 | *port = (in_port_t)_crywrap_port_get (&tmp[1]); |
353 | s_ip = strndup (ip, tmp - ip); |
354 | } |
355 | |
356 | if (!*port) |
357 | return -1; |
358 | |
359 | if (host) |
360 | *host = strdup (s_ip); |
361 | |
362 | return _crywrap_addr_get (s_ip, addr); |
363 | } |
364 | |
365 | /** Argument parsing routine. |
366 | * Used by the argp suite. |
367 | */ |
368 | static error_t |
369 | _crywrap_config_parse_opt (int key, char *arg, struct argp_state *state) |
370 | { |
371 | crywrap_config_t *cfg = (crywrap_config_t *)state->input; |
372 | int ret; |
373 | |
374 | switch (key) |
375 | { |
376 | case 'D': |
377 | cfg->debug = 1; |
378 | cry_log = debug_log; |
379 | cry_error = debug_log; |
380 | break; |
381 | case 'd': |
382 | if (_crywrap_parse_ip (arg, &cfg->dest.port, &cfg->dest.addr, |
383 | &cfg->dest.host) < 0) |
384 | argp_error (state, "Could not resolve address: `%s'", arg); |
385 | break; |
386 | case 'l': |
387 | if (_crywrap_parse_ip (arg, &cfg->listen.port, |
388 | &cfg->listen.addr, NULL((void*)0)) < 0) |
389 | argp_error (state, "Could not resolve address: `%s'", arg); |
390 | break; |
391 | case 'u': |
392 | cfg->uid = atoi (arg); |
393 | break; |
394 | case 'P': |
395 | if (arg && *arg) |
396 | cfg->pidfile = strdup (arg); |
397 | else |
398 | cfg->pidfile = NULL((void*)0); |
399 | break; |
400 | case 'r': |
401 | if (arg && *arg) |
402 | { |
403 | dh_file = load_file(arg); |
404 | if (dh_file.data == NULL((void*)0)) |
405 | argp_error (state, "error loading Diffie Hellman parameters file: %s.", arg); |
406 | } |
407 | break; |
408 | case 'p': |
409 | if (arg && *arg) |
410 | { |
411 | const char* pos; |
412 | ret = gnutls_priority_init(&cfg->priority, arg, &pos); |
413 | if (ret < 0) |
414 | argp_error (state, "error in priority string at: %s.", pos); |
415 | } |
416 | break; |
417 | case 'c': |
418 | if (arg && *arg) |
419 | pem_cert = strdup (arg); |
420 | break; |
421 | case 'k': |
422 | if (arg && *arg) |
423 | pem_key = strdup (arg); |
424 | break; |
425 | |
426 | break; |
427 | case 'i': |
428 | cfg->inetd = 1; |
429 | break; |
430 | case 'a': |
431 | { |
432 | const char* pos; |
433 | ret = gnutls_priority_init(&cfg->priority, "NORMAL:+ANON-ECDH:+ANON-DH", &pos); |
434 | if (ret < 0) |
435 | argp_error (state, "error in priority string at: %s.", pos); |
436 | } |
437 | cfg->verify = 0; |
438 | cfg->anon = 1; |
439 | break; |
440 | case 'v': |
441 | cfg->verify = (arg) ? atoi (arg) : 1; |
442 | break; |
443 | case 'z': |
444 | ret = gnutls_certificate_set_x509_trust_file (cred, arg, |
445 | GNUTLS_X509_FMT_PEM); |
446 | if (ret < 0) |
447 | argp_error (state, "error reading X.509 CA file: %s.", gnutls_strerror(ret)); |
448 | break; |
449 | |
450 | case ARGP_KEY_END0x1000001: |
451 | if (!cfg->inetd) |
452 | { |
453 | if (!cfg->listen.addr || !cfg->dest.addr) |
454 | argp_error |
455 | (state, |
456 | "a listening and a destination address must be set!"); |
457 | } |
458 | else |
459 | if (!cfg->dest.addr) |
460 | argp_error (state, "a destination address must be set!"); |
461 | if (cfg->anon) |
462 | break; |
463 | if (pem_cert == NULL((void*)0) || pem_key == NULL((void*)0)) |
464 | ret = gnutls_certificate_set_x509_key_file (cred, _CRYWRAP_PEMFILE"/usr/local/etc" "/crywrap" "/server.pem", |
465 | _CRYWRAP_PEMFILE"/usr/local/etc" "/crywrap" "/server.pem", |
466 | GNUTLS_X509_FMT_PEM); |
467 | else |
468 | ret = gnutls_certificate_set_x509_key_file (cred, pem_cert, pem_key, |
469 | GNUTLS_X509_FMT_PEM); |
470 | |
471 | if (ret < 0) |
472 | argp_error (state, "Error reading X.509 key or certificate file: %s", gnutls_strerror(ret)); |
473 | break; |
474 | default: |
475 | return ARGP_ERR_UNKNOWN7; |
476 | } |
477 | |
478 | return 0; |
479 | } |
480 | |
481 | /** Configuration parsing. |
482 | * Sets up the default values, and parses the command-line options |
483 | * using argp. |
484 | * |
485 | * @note Does not return if an error occurred. |
486 | */ |
487 | static crywrap_config_t * |
488 | _crywrap_config_parse (int argc, char **argv) |
489 | { |
490 | crywrap_config_t *config = |
491 | (crywrap_config_t *)malloc (sizeof (crywrap_config_t)); |
492 | |
493 | if (config == NULL((void*)0)) |
494 | return NULL((void*)0); |
495 | |
496 | config->listen.port = 0; |
497 | config->listen.addr = NULL((void*)0); |
498 | config->dest.port = 0; |
499 | config->dest.addr = NULL((void*)0); |
500 | config->priority = NULL((void*)0); |
501 | config->uid = _CRYWRAP_UID65534; |
502 | config->pidfile = _CRYWRAP_PIDFILE"/var/run/crywrap.pid"; |
503 | config->inetd = 0; |
504 | config->anon = 0; |
505 | config->verify = 0; |
506 | |
507 | argp_parse (&_crywrap_argp, argc, argv, 0, 0, config); |
508 | |
509 | if (config->priority == NULL((void*)0)) |
510 | gnutls_priority_init(&config->priority, "NORMAL", NULL((void*)0)); |
511 | |
512 | return config; |
513 | } |
514 | /** @} */ |
515 | |
516 | /** @defgroup tls Lower-level TLS routines. |
517 | * @{ |
518 | */ |
519 | |
520 | /** Create a GNUTLS session. |
521 | * Initialises the cyphers and the session database for a new TLS |
522 | * session. |
523 | * |
524 | * @returns The newly created TLS session. |
525 | */ |
526 | static gnutls_session_t |
527 | _crywrap_tls_session_create (const crywrap_config_t *config) |
528 | { |
529 | gnutls_session_t session; |
530 | int ret; |
531 | |
532 | gnutls_init (&session, GNUTLS_SERVER1); |
533 | |
534 | if (config->anon) { |
535 | gnutls_credentials_set (session, GNUTLS_CRD_ANON, cred); |
536 | } else { |
537 | gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, cred); |
538 | } |
539 | |
540 | ret = gnutls_priority_set(session, config->priority); |
541 | if (ret < 0) |
542 | { |
543 | cry_error ("Error setting priority %s: ", gnutls_strerror(ret)); |
544 | exit (4); |
545 | } |
546 | |
547 | if (config->verify==1) |
548 | gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); |
549 | else if (config->verify==2) |
550 | gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUIRE); |
551 | |
552 | return session; |
553 | } |
554 | |
555 | /** Generate initial DH and RSA params. |
556 | * Loads the pre-generated DH primes. |
557 | */ |
558 | static void |
559 | _crywrap_tls_init (void) |
560 | { |
561 | |
562 | gnutls_dh_params_init (&dh_params); |
563 | gnutls_dh_params_import_pkcs3 (dh_params, &dh_file, GNUTLS_X509_FMT_PEM); |
564 | |
565 | gnutls_certificate_set_dh_params (cred, dh_params); |
566 | } |
567 | /** @} */ |
568 | |
569 | /** @defgroup networking Networking |
570 | * @{ |
571 | */ |
572 | |
573 | /** Bind to an address. |
574 | * This one binds to an address, handles errors and anything that may |
575 | * arise. |
576 | * |
577 | * @param ai is the address information. |
578 | * @param listen_port is the port to bind to, and listen on. |
579 | * |
580 | * @returns The bound filedescriptor, or -1 on error. |
581 | */ |
582 | static int |
583 | _crywrap_bind (const struct addrinfo *ai, int listen_port) |
584 | { |
585 | int ret; |
586 | const int one = 1; |
587 | int listenfd; |
588 | char sock_name[NI_MAXHOST1025]; |
589 | |
590 | listenfd = socket (ai->ai_family, SOCK_STREAMSOCK_STREAM, IPPROTO_IPIPPROTO_IP); |
591 | if (listenfd == -1) |
592 | { |
593 | cry_error ("socket: %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
594 | return -1; |
595 | } |
596 | |
597 | memset (sock_name, 0, sizeof (sock_name)); |
598 | getnameinfo ((struct sockaddr *)ai->ai_addr, ai->ai_addrlen, sock_name, |
599 | sizeof (sock_name), NULL((void*)0), 0, NI_NUMERICHOST1); |
600 | |
601 | switch (ai->ai_family) |
602 | { |
603 | case AF_INET610: |
604 | ((struct sockaddr_in6 *)(ai->ai_addr))->sin6_port = listen_port; |
605 | break; |
606 | case AF_INET2: |
607 | ((struct sockaddr_in *)(ai->ai_addr))->sin_port = listen_port; |
608 | break; |
609 | } |
610 | |
611 | ret = setsockopt (listenfd, SOL_SOCKET1, SO_REUSEADDR2, |
612 | &one, sizeof (one)); |
613 | if (ret != 0) |
614 | { |
615 | cry_error ("setsockopt: %s (%s)", strerrorrpl_strerror (errno(*__errno_location ())), sock_name); |
616 | return -1; |
617 | } |
618 | |
619 | ret = bind (listenfd, ai->ai_addr, ai->ai_addrlen); |
620 | if (ret != 0) |
621 | { |
622 | cry_error ("bind to %s failed: %s", sock_name, strerrorrpl_strerror (errno(*__errno_location ()))); |
623 | return -1; |
624 | } |
625 | |
626 | if (listen (listenfd, _CRYWRAP_MAXCONN1024) != 0) |
627 | { |
628 | cry_error ("listen on %s failed: %s", sock_name, strerrorrpl_strerror (errno(*__errno_location ()))); |
629 | return -1; |
630 | } |
631 | |
632 | cry_log ("Socket bound to port %d on %s.", ntohs (listen_port), sock_name); |
633 | |
634 | return listenfd; |
635 | } |
636 | |
637 | /** Set up a listening socket. |
638 | * Sets up a listening socket on all the required addresses. |
639 | * |
640 | * @param config holds the CryWrap configuration, from where the |
641 | * listen address and port will be extracted. |
642 | * |
643 | * @returns The listening FD on success, -1 on error. |
644 | */ |
645 | static int |
646 | _crywrap_listen (const crywrap_config_t *config) |
647 | { |
648 | struct addrinfo *cur; |
649 | int ret; |
650 | |
651 | cur = calloc (1, sizeof (struct addrinfo)); |
652 | if (cur == NULL((void*)0)) |
653 | return -1; |
654 | |
655 | cur->ai_family = config->listen.addr->ss_family; |
656 | |
657 | switch (cur->ai_family) |
658 | { |
659 | case AF_INET610: |
660 | cur->ai_addrlen = sizeof (struct sockaddr_in6); |
661 | break; |
662 | case AF_INET2: |
663 | cur->ai_addrlen = sizeof (struct sockaddr_in); |
664 | break; |
665 | } |
666 | |
667 | cur->ai_addr = malloc (cur->ai_addrlen); |
668 | if (cur->ai_addr == NULL((void*)0)) |
669 | return -1; |
670 | |
671 | memcpy (cur->ai_addr, config->listen.addr, cur->ai_addrlen); |
672 | |
673 | ret = _crywrap_bind (cur, htons (config->listen.port)); |
674 | free (cur->ai_addr); |
675 | free (cur); |
676 | |
677 | return ret; |
678 | } |
679 | |
680 | /** Connect to a remote server. |
681 | * Estabilishes a connection to a remote server, and handles all |
682 | * errors and anything that may arise during this process. |
683 | * |
684 | * @param addr is the address of the remote server. |
685 | * @param port is the port to connect to. |
686 | * |
687 | * @returns the connected socket on success, otherwise it exits. |
688 | */ |
689 | static int |
690 | _crywrap_remote_connect (const struct sockaddr_storage *addr, int port) |
691 | { |
692 | struct addrinfo *cur; |
693 | int sock; |
694 | |
695 | cur = calloc (1, sizeof (struct addrinfo)); |
696 | if (cur == NULL((void*)0)) |
697 | return -1; |
698 | |
699 | cur->ai_family = addr->ss_family; |
700 | |
701 | switch (cur->ai_family) |
702 | { |
703 | case AF_INET610: |
704 | cur->ai_addrlen = sizeof (struct sockaddr_in6); |
705 | break; |
706 | case AF_INET2: |
707 | cur->ai_addrlen = sizeof (struct sockaddr_in); |
708 | break; |
709 | } |
710 | |
711 | cur->ai_addr = malloc (cur->ai_addrlen); |
712 | if (cur->ai_addr == NULL((void*)0)) |
713 | return -1; |
714 | |
715 | memcpy (cur->ai_addr, addr, cur->ai_addrlen); |
716 | |
717 | switch (cur->ai_family) |
718 | { |
719 | case AF_INET610: |
720 | ((struct sockaddr_in6 *)(cur->ai_addr))->sin6_port = port; |
721 | break; |
722 | case AF_INET2: |
723 | ((struct sockaddr_in *)(cur->ai_addr))->sin_port = port; |
724 | break; |
725 | } |
726 | |
727 | sock = socket (cur->ai_family, SOCK_STREAMSOCK_STREAM, IPPROTO_IPIPPROTO_IP); |
728 | if (sock < 0) |
729 | { |
730 | cry_error ("socket(): %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
731 | exit (1); |
732 | } |
733 | |
734 | if (connect (sock, cur->ai_addr, cur->ai_addrlen) < 0) |
735 | { |
736 | cry_error ("connect(): %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
737 | exit (1); |
738 | } |
739 | |
740 | free (cur->ai_addr); |
741 | free (cur); |
742 | |
743 | return sock; |
744 | } |
745 | |
746 | /** @} */ |
747 | |
748 | /** @defgroup crywrap Main CryWrap code. |
749 | * @{ |
750 | */ |
751 | |
752 | /** Drop privileges. |
753 | * Drop privileges, if running as root. |
754 | * Upon failure, it will make CryWrap exit. |
755 | */ |
756 | static void |
757 | _crywrap_privs_drop (const crywrap_config_t *config) |
758 | { |
759 | struct passwd *pwd; |
760 | |
761 | if (getuid () != 0) |
762 | { |
763 | cry_log ("%s", "Not running as root, not dropping privileges."); |
764 | return; |
765 | } |
766 | |
767 | if ((pwd = getpwuid (config->uid)) == NULL((void*)0)) |
768 | { |
769 | cry_error ("getpwuid(): %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
770 | exit (1); |
771 | } |
772 | |
773 | if (initgroups (pwd->pw_name, pwd->pw_gid) == -1) |
774 | { |
775 | cry_error ("initgroups(): %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
776 | exit (1); |
777 | } |
778 | |
779 | if (setgid (pwd->pw_gid) == -1) |
780 | { |
781 | cry_error ("setgid(): %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
782 | exit (1); |
783 | } |
784 | |
785 | if (setuid (config->uid)) |
786 | { |
787 | cry_error ("setuid(): %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
788 | exit (1); |
789 | } |
790 | } |
791 | |
792 | /** Set up the PID file. |
793 | * Checks if a #pidfile already exists, and create one - containing the |
794 | * current PID - if one does not. |
795 | * |
796 | * @note Exits upon error. |
797 | */ |
798 | static void |
799 | _crywrap_setup_pidfile (const crywrap_config_t *config) |
800 | { |
801 | char mypid[128]; |
802 | int pidfilefd; |
803 | |
804 | if (!config->pidfile || !*(config->pidfile)) |
805 | return; |
806 | |
807 | if (!access (config->pidfile, F_OK0)) |
808 | { |
809 | cry_error ("Pidfile (%s) already exists. Exiting.", config->pidfile); |
810 | exit (1); |
811 | } |
812 | if ((pidfilefd = open (config->pidfile, |
813 | O_WRONLY01 | O_CREAT0100 | O_TRUNC01000, 0644)) == -1) |
814 | { |
815 | cry_error ("Cannot create pidfile (%s): %s.\n", config->pidfile, |
816 | strerrorrpl_strerror (errno(*__errno_location ()))); |
817 | exit (1); |
818 | } |
819 | fchown (pidfilefd, config->uid, (gid_t)-1); |
820 | |
821 | main_pid = getpid (); |
822 | snprintf (mypid, sizeof (mypid), "%d\n", main_pid); |
823 | write (pidfilefd, mypid, strlen (mypid)); |
824 | close (pidfilefd); |
825 | pidfile = config->pidfile; |
826 | } |
827 | |
828 | |
829 | /** Handles one client. |
830 | * This one connects to the remote server, and proxies every traffic |
831 | * between our client and the server. |
832 | * |
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. |
836 | * |
837 | * @note Exits on error. |
838 | */ |
839 | static int |
840 | _crywrap_do_one (const crywrap_config_t *config, int insock, int outsock) |
841 | { |
842 | int sock, ret, tls_pending; |
843 | gnutls_session_t session; |
844 | char buffer[_CRYWRAP_MAXBUF64 * 1024 + 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_MAXHOST1025]; |
850 | |
851 | /* Log the connection */ |
852 | if (getpeername (insock, (struct sockaddr *)&faddr, &socklen) != 0) |
853 | cry_error ("getpeername(): %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
854 | else |
855 | { |
856 | getnameinfo ((struct sockaddr *)&faddr, |
857 | sizeof (struct sockaddr_storage), peer_name, |
858 | sizeof (peer_name), NULL((void*)0), 0, NI_NUMERICHOST1); |
859 | cry_log ("Accepted connection from %s on %d to %s/%d", |
860 | peer_name, insock, config->dest.host, |
861 | config->dest.port); |
862 | } |
863 | |
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); |
869 | |
870 | do |
871 | { |
872 | ret = gnutls_handshake(session); |
873 | } |
874 | while (ret == GNUTLS_E_AGAIN-28 || ret == GNUTLS_E_INTERRUPTED-52); |
875 | |
876 | if (ret < 0) |
877 | { |
878 | cry_error ("Handshake failed: %s", gnutls_strerror (ret)); |
879 | gnutls_alert_send_appropriate(session, ret); |
880 | goto error; |
881 | } |
882 | |
883 | /* Verify the client's certificate, if any. */ |
884 | if (config->verify) |
885 | { |
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)); |
890 | |
891 | if (ret == 0 && status != 0) |
892 | { |
893 | if (status & GNUTLS_CERT_INVALID) |
894 | cry_log ("%s", "Client certificate not trusted or invalid"); |
895 | } |
896 | |
897 | if (config->verify > 0 && status != 0) |
898 | { |
899 | ret = -1; |
900 | gnutls_alert_send( session, GNUTLS_AL_FATAL, GNUTLS_A_INSUFFICIENT_SECURITY); |
901 | goto error; |
902 | } |
903 | } |
904 | |
905 | /* Connect to the remote host */ |
906 | sock = _crywrap_remote_connect (config->dest.addr, |
907 | htons (config->dest.port)); |
908 | |
909 | for (;;) |
910 | { |
911 | FD_ZERO (&fdset)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosq" : "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) / sizeof (__fd_mask)), "1" (&((&fdset)->fds_bits)[0 ]) : "memory"); } while (0); |
912 | FD_SET (insock, &fdset)(((&fdset)->fds_bits)[((insock) / (8 * (int) sizeof (__fd_mask )))] |= ((__fd_mask) 1 << ((insock) % (8 * (int) sizeof (__fd_mask))))); |
913 | FD_SET (sock, &fdset)(((&fdset)->fds_bits)[((sock) / (8 * (int) sizeof (__fd_mask )))] |= ((__fd_mask) 1 << ((sock) % (8 * (int) sizeof ( __fd_mask))))); |
914 | |
915 | memset (buffer, 0, _CRYWRAP_MAXBUF64 * 1024 + 1); |
916 | |
917 | tls_pending = 0; |
918 | |
919 | if (gnutls_record_check_pending(session) > 0) |
920 | tls_pending = 1; |
921 | else |
922 | { |
923 | select (sock + 1, &fdset, NULL((void*)0), NULL((void*)0), NULL((void*)0)); |
924 | if (FD_ISSET (insock, &fdset)((((&fdset)->fds_bits)[((insock) / (8 * (int) sizeof ( __fd_mask)))] & ((__fd_mask) 1 << ((insock) % (8 * ( int) sizeof (__fd_mask))))) != 0)) |
925 | tls_pending = 1; |
926 | } |
927 | /* TLS client */ |
928 | if (tls_pending != 0) |
929 | { |
930 | ret = gnutls_record_recv (session, buffer, _CRYWRAP_MAXBUF64 * 1024); |
931 | if (ret == 0) |
932 | { |
933 | cry_log ("%s", "Peer has closed the GNUTLS connection"); |
934 | break; |
935 | } |
936 | else if (ret < 0) |
937 | { |
938 | cry_log ("Received corrupted data: %s.", |
939 | gnutls_strerror (ret)); |
940 | break; |
941 | } |
942 | else |
943 | send (sock, buffer, ret, 0); |
944 | } |
945 | |
946 | /* Remote server */ |
947 | if (FD_ISSET (sock, &fdset)((((&fdset)->fds_bits)[((sock) / (8 * (int) sizeof (__fd_mask )))] & ((__fd_mask) 1 << ((sock) % (8 * (int) sizeof (__fd_mask))))) != 0)) |
948 | { |
949 | ret = recv (sock, buffer, _CRYWRAP_MAXBUF64 * 1024, 0); |
950 | if (ret == 0) |
951 | { |
952 | cry_log ("%s", "Server has closed the connection"); |
953 | break; |
954 | } |
955 | else if (ret < 0) |
956 | { |
957 | cry_log ("Received corrupted data: %s.", strerrorrpl_strerror (errno(*__errno_location ()))); |
958 | break; |
959 | } |
960 | else |
961 | { |
962 | int r, o = 0; |
963 | |
964 | do |
965 | { |
966 | r = gnutls_record_send (session, &buffer[o], ret - o); |
967 | o += r; |
968 | } while (r > 0 && ret > o); |
969 | |
970 | if (r < 0) |
971 | cry_log ("Received corrupt data: %s", gnutls_strerror (r)); |
972 | } |
973 | } |
974 | } |
975 | |
976 | error: |
977 | gnutls_bye (session, GNUTLS_SHUT_WR); |
978 | gnutls_deinit (session); |
979 | close (insock); |
980 | close (outsock); |
981 | |
982 | return (ret == 0) ? 0 : 1; |
983 | } |
984 | |
985 | /** CryWrap entry point. |
986 | * This is the main entry point - controls the whole program and so |
987 | * on... |
988 | */ |
989 | int |
990 | main (int argc, char **argv, char **envp) |
991 | { |
992 | crywrap_config_t *config; |
993 | int server_socket; |
994 | |
995 | openlog (__CRYWRAP__"crywrap", LOG_PID0x01, LOG_DAEMON(3<<3)); |
996 | |
997 | gnutls_global_set_audit_log_function (tls_audit_log_func); |
998 | |
999 | if (gnutls_global_init () < 0) |
1000 | { |
1001 | cry_error ("%s", "Global TLS state initialisation failed."); |
1002 | exit (1); |
1003 | } |
1004 | if (gnutls_certificate_allocate_credentials (&cred) < 0) |
1005 | { |
1006 | cry_error ("%s", "Couldn't allocate credentials."); |
1007 | exit (1); |
1008 | } |
1009 | |
1010 | stringprep_locale_charset (); |
1011 | |
1012 | config = _crywrap_config_parse (argc, argv); |
1013 | set_program_name(__CRYWRAP__"crywrap"); |
1014 | |
1015 | _crywrap_tls_init (); |
1016 | |
1017 | if (config->inetd) |
1018 | { |
1019 | _crywrap_privs_drop (config); |
1020 | exit (_crywrap_do_one (config, 0, 1)); |
1021 | } |
1022 | |
1023 | if (!config->debug) |
1024 | if (daemon (0, 0)) |
1025 | { |
1026 | cry_error ("daemon: %s", strerrorrpl_strerror (errno(*__errno_location ()))); |
1027 | exit (1); |
1028 | } |
1029 | |
1030 | cry_log ("%s", "Crywrap starting..."); |
1031 | |
1032 | server_socket = _crywrap_listen (config); |
1033 | if (server_socket < 0) |
1034 | exit (1); |
1035 | |
1036 | if (!config->debug) _crywrap_setup_pidfile (config); |
1037 | _crywrap_privs_drop (config); |
1038 | |
1039 | signal (SIGTERM15, _crywrap_sighandler); |
1040 | signal (SIGQUIT3, _crywrap_sighandler); |
1041 | signal (SIGSEGV11, _crywrap_sighandler); |
1042 | signal (SIGPIPE13, SIG_IGN((__sighandler_t) 1)); |
1043 | signal (SIGHUP1, SIG_IGN((__sighandler_t) 1)); |
1044 | signal (SIGCHLD17, _crywrap_sigchld_handler); |
1045 | |
1046 | cry_log ("%s", "Accepting connections"); |
1047 | |
1048 | |
1049 | for (;;) |
1050 | { |
1051 | int csock; |
1052 | int child; |
1053 | |
1054 | csock = accept (server_socket, NULL((void*)0), NULL((void*)0)); |
1055 | if (csock < 0) |
1056 | continue; |
1057 | |
1058 | child = fork (); |
1059 | switch (child) |
1060 | { |
1061 | case 0: |
1062 | exit (_crywrap_do_one (config, csock, csock)); |
1063 | break; |
1064 | case -1: |
1065 | cry_error ("%s", "Forking error."); |
1066 | exit (1); |
1067 | break; |
1068 | } |
1069 | close(csock); |
1070 | } |
1071 | |
1072 | return 0; |
1073 | } |
1074 | |
1075 | static int system_log(const char* fmt, ...) |
1076 | { |
1077 | va_list args; |
1078 | |
1079 | va_start (args, fmt)__builtin_va_start(args, fmt); |
1080 | vsyslog(LOG_NOTICE5, fmt, args); |
1081 | va_end (args)__builtin_va_end(args); |
1082 | |
1083 | return 0; |
1084 | } |
1085 | |
1086 | static int system_log_error(const char* fmt, ...) |
1087 | { |
1088 | va_list args; |
1089 | |
1090 | va_start (args, fmt)__builtin_va_start(args, fmt); |
1091 | vsyslog(LOG_ERR3, fmt, args); |
1092 | va_end (args)__builtin_va_end(args); |
1093 | |
1094 | return 0; |
1095 | } |
1096 | |
1097 | static int debug_log(const char* fmt, ...) |
1098 | { |
1099 | va_list args; |
1100 | |
1101 | va_start (args, fmt)__builtin_va_start(args, fmt); |
1102 | vprintfrpl_vprintf(fmt, args); |
1103 | puts(""); |
1104 | va_end (args)__builtin_va_end(args); |
1105 | |
1106 | return 0; |
1107 | } |
1108 | |
1109 | /** @} */ |
1110 |