Fix use of deprecated types, for now and the future.
[gnutls.git] / src / serv.c
blobede5117b883b973bb0a76eb361429c1155c426b3
1 /*
2 * Copyright (C) 2004, 2006, 2007, 2008, 2009 Free Software Foundation
3 * Copyright (C) 2001,2002 Paul Sheer
4 * Portions Copyright (C) 2002,2003 Nikos Mavrogiannopoulos
6 * This file is part of GNUTLS.
8 * GNUTLS 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.
13 * GNUTLS 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.
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/>.
22 /* This server is heavily modified for GNUTLS by Nikos Mavrogiannopoulos
23 * (which means it is quite unreadable)
26 #include <config.h>
28 #include "common.h"
29 #include "serv-gaa.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <string.h>
35 #include <gnutls/gnutls.h>
36 #include <gcrypt.h>
37 #include <gnutls/extra.h>
38 #include <gnutls/openpgp.h>
39 #include <sys/time.h>
40 #include <sys/select.h>
41 #include <fcntl.h>
42 #include <list.h>
43 #include <netdb.h>
44 #include <error.h>
46 /* Gnulib portability files. */
47 #include "progname.h"
48 #include "version-etc.h"
49 #include "read-file.h"
50 #include "minmax.h"
51 #include "sockets.h"
53 /* konqueror cannot handle sending the page in multiple
54 * pieces.
56 /* global stuff */
57 static int generate = 0;
58 static int http = 0;
59 static int port = 0;
60 static int x509ctype;
61 static int debug;
63 int verbose;
64 static int nodb;
65 static int noticket;
66 int require_cert;
67 int disable_client_cert;
69 char *psk_passwd;
70 char *srp_passwd;
71 char *srp_passwd_conf;
72 char *pgp_keyring;
73 char *pgp_keyfile;
74 char *pgp_certfile;
75 char *x509_keyfile;
76 char *x509_certfile;
77 char *x509_dsakeyfile;
78 char *x509_dsacertfile;
79 char *x509_cafile;
80 char *dh_params_file;
81 char *x509_crlfile = NULL;
83 gnutls_datum_t session_ticket_key;
85 /* end of globals */
87 /* This is a sample TCP echo server.
88 * This will behave as an http server if any argument in the
89 * command line is present
92 #define SMALL_READ_TEST (2147483647)
94 #define GERR(ret) fprintf(stdout, "Error: %s\n", safe_strerror(ret))
96 #define HTTP_END "</BODY></HTML>\n\n"
98 #define HTTP_UNIMPLEMENTED "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<HTML><HEAD>\r\n<TITLE>501 Method Not Implemented</TITLE>\r\n</HEAD><BODY>\r\n<H1>Method Not Implemented</H1>\r\n<HR>\r\n</BODY></HTML>\r\n"
99 #define HTTP_OK "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n"
101 #define HTTP_BEGIN HTTP_OK \
102 "\n" \
103 "<HTML><BODY>\n" \
104 "<CENTER><H1>This is <a href=\"http://www.gnu.org/software/gnutls\">" \
105 "GNUTLS</a></H1></CENTER>\n\n"
107 /* These are global */
108 gnutls_srp_server_credentials_t srp_cred = NULL;
109 gnutls_psk_server_credentials_t psk_cred = NULL;
110 gnutls_anon_server_credentials_t dh_cred = NULL;
111 gnutls_certificate_credentials_t cert_cred = NULL;
113 static gaainfo info;
115 const int ssl_session_cache = 128;
117 static void wrap_db_init (void);
118 static void wrap_db_deinit (void);
119 static int wrap_db_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data);
120 static gnutls_datum_t wrap_db_fetch (void *dbf, gnutls_datum_t key);
121 static int wrap_db_delete (void *dbf, gnutls_datum_t key);
124 #define HTTP_STATE_REQUEST 1
125 #define HTTP_STATE_RESPONSE 2
126 #define HTTP_STATE_CLOSING 3
128 LIST_TYPE_DECLARE (listener_item, char *http_request;
129 char *http_response; int request_length;
130 int response_length; int response_written;
131 int http_state; int listen_socket;
132 int fd; gnutls_session_t tls_session; int handshake_ok;);
134 static const char *
135 safe_strerror (int value)
137 const char *ret = gnutls_strerror (value);
138 if (ret == NULL)
139 ret = str_unknown;
140 return ret;
143 static void
144 listener_free (listener_item * j)
147 free (j->http_request);
148 free (j->http_response);
149 if (j->fd >= 0)
151 gnutls_bye (j->tls_session, GNUTLS_SHUT_WR);
152 shutdown (j->fd, 2);
153 close (j->fd);
154 gnutls_deinit (j->tls_session);
159 /* we use primes up to 1024 in this server.
160 * otherwise we should add them here.
163 gnutls_dh_params_t dh_params = NULL;
164 gnutls_rsa_params_t rsa_params = NULL;
166 static int
167 generate_dh_primes (void)
169 int prime_bits = 768;
171 if (gnutls_dh_params_init (&dh_params) < 0)
173 fprintf (stderr, "Error in dh parameter initialization\n");
174 exit (1);
177 /* Generate Diffie-Hellman parameters - for use with DHE
178 * kx algorithms. These should be discarded and regenerated
179 * once a week or once a month. Depends on the
180 * security requirements.
182 printf
183 ("Generating Diffie-Hellman parameters [%d]. Please wait...\n",
184 prime_bits);
185 fflush (stdout);
187 if (gnutls_dh_params_generate2 (dh_params, prime_bits) < 0)
189 fprintf (stderr, "Error in prime generation\n");
190 exit (1);
193 return 0;
196 static void
197 read_dh_params (void)
199 char tmpdata[2048];
200 int size;
201 gnutls_datum_t params;
202 FILE *fd;
204 if (gnutls_dh_params_init (&dh_params) < 0)
206 fprintf (stderr, "Error in dh parameter initialization\n");
207 exit (1);
210 /* read the params file
212 fd = fopen (dh_params_file, "r");
213 if (fd == NULL)
215 fprintf (stderr, "Could not open %s\n", dh_params_file);
216 exit (1);
219 size = fread (tmpdata, 1, sizeof (tmpdata) - 1, fd);
220 tmpdata[size] = 0;
221 fclose (fd);
223 params.data = (unsigned char *) tmpdata;
224 params.size = size;
226 size =
227 gnutls_dh_params_import_pkcs3 (dh_params, &params, GNUTLS_X509_FMT_PEM);
229 if (size < 0)
231 fprintf (stderr, "Error parsing dh params: %s\n", safe_strerror (size));
232 exit (1);
235 printf ("Read Diffie-Hellman parameters.\n");
236 fflush (stdout);
240 static char pkcs3[] =
241 "-----BEGIN DH PARAMETERS-----\n"
242 "MIGGAoGAtkxw2jlsVCsrfLqxrN+IrF/3W8vVFvDzYbLmxi2GQv9s/PQGWP1d9i22\n"
243 "P2DprfcJknWt7KhCI1SaYseOQIIIAYP78CfyIpGScW/vS8khrw0rlQiyeCvQgF3O\n"
244 "GeGOEywcw+oQT4SmFOD7H0smJe2CNyjYpexBXQ/A0mbTF9QKm1cCAQU=\n"
245 "-----END DH PARAMETERS-----\n";
247 static int
248 static_dh_params (void)
250 gnutls_datum_t params = { pkcs3, sizeof (pkcs3) };
251 int ret;
253 if (gnutls_dh_params_init (&dh_params) < 0)
255 fprintf (stderr, "Error in dh parameter initialization\n");
256 exit (1);
259 ret = gnutls_dh_params_import_pkcs3 (dh_params, &params,
260 GNUTLS_X509_FMT_PEM);
262 if (ret < 0)
264 fprintf (stderr, "Error parsing dh params: %s\n", safe_strerror (ret));
265 exit (1);
268 printf ("Set static Diffie-Hellman parameters, consider --dhparams.\n");
270 return 0;
273 static int
274 get_params (gnutls_session_t session, gnutls_params_type_t type,
275 gnutls_params_st * st)
278 if (type == GNUTLS_PARAMS_RSA_EXPORT)
280 if (rsa_params == NULL)
281 return -1;
282 st->params.rsa_export = rsa_params;
284 else if (type == GNUTLS_PARAMS_DH)
286 if (dh_params == NULL)
287 return -1;
288 st->params.dh = dh_params;
290 else
291 return -1;
293 st->type = type;
294 st->deinit = 0;
296 return 0;
299 static int
300 generate_rsa_params (void)
302 if (gnutls_rsa_params_init (&rsa_params) < 0)
304 fprintf (stderr, "Error in rsa parameter initialization\n");
305 exit (1);
308 /* Generate RSA parameters - for use with RSA-export
309 * cipher suites. These should be discarded and regenerated
310 * once a day, once every 500 transactions etc. Depends on the
311 * security requirements.
313 printf ("Generating temporary RSA parameters. Please wait...\n");
314 fflush (stdout);
316 if (gnutls_rsa_params_generate2 (rsa_params, 512) < 0)
318 fprintf (stderr, "Error in rsa parameter generation\n");
319 exit (1);
322 return 0;
325 LIST_DECLARE_INIT (listener_list, listener_item, listener_free);
327 static int protocol_priority[PRI_MAX];
328 static int kx_priority[PRI_MAX];
329 static int cipher_priority[PRI_MAX];
330 static int comp_priority[PRI_MAX];
331 static int mac_priority[PRI_MAX];
332 static int cert_type_priority[PRI_MAX];
334 #if ENABLE_OPRFI
336 oprfi_callback (gnutls_session_t session,
337 void *userdata,
338 size_t oprfi_len,
339 const unsigned char *in_oprfi, unsigned char *out_oprfi)
341 size_t ourlen = strlen (info.opaque_prf_input);
342 size_t i;
344 printf ("- Received Opaque PRF data of %d bytes\n", oprfi_len);
345 printf (" data: ");
346 for (i = 0; i < oprfi_len; i++)
347 printf ("%02x", in_oprfi[i]);
348 printf ("\n");
350 memset (out_oprfi, 0, oprfi_len);
351 strncpy (out_oprfi, info.opaque_prf_input, oprfi_len);
353 return 0;
355 #endif
357 static gnutls_session_t
358 initialize_session (void)
360 gnutls_session_t session;
361 const char *err;
363 gnutls_init (&session, GNUTLS_SERVER);
365 /* allow the use of private ciphersuites.
367 gnutls_handshake_set_private_extensions (session, 1);
369 if (nodb == 0)
371 gnutls_db_set_retrieve_function (session, wrap_db_fetch);
372 gnutls_db_set_remove_function (session, wrap_db_delete);
373 gnutls_db_set_store_function (session, wrap_db_store);
374 gnutls_db_set_ptr (session, NULL);
376 #ifdef ENABLE_SESSION_TICKET
377 if (noticket == 0)
378 gnutls_session_ticket_enable_server (session, &session_ticket_key);
379 #endif
381 if (gnutls_priority_set_direct (session, info.priorities, &err) < 0)
383 fprintf (stderr, "Syntax error at: %s\n", err);
384 exit (1);
387 if (cipher_priority[0])
388 gnutls_cipher_set_priority (session, cipher_priority);
389 if (comp_priority[0])
390 gnutls_compression_set_priority (session, comp_priority);
391 if (kx_priority[0])
392 gnutls_kx_set_priority (session, kx_priority);
393 if (protocol_priority[0])
394 gnutls_protocol_set_priority (session, protocol_priority);
395 if (mac_priority[0])
396 gnutls_mac_set_priority (session, mac_priority);
397 if (cert_type_priority[0])
398 gnutls_certificate_type_set_priority (session, cert_type_priority);
400 gnutls_credentials_set (session, GNUTLS_CRD_ANON, dh_cred);
402 if (srp_cred != NULL)
403 gnutls_credentials_set (session, GNUTLS_CRD_SRP, srp_cred);
405 if (psk_cred != NULL)
406 gnutls_credentials_set (session, GNUTLS_CRD_PSK, psk_cred);
408 if (cert_cred != NULL)
409 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, cert_cred);
411 if (disable_client_cert)
412 gnutls_certificate_server_set_request (session, GNUTLS_CERT_IGNORE);
413 else
415 if (require_cert)
416 gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUIRE);
417 else
418 gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
421 #ifdef ENABLE_OPRFI
422 if (info.opaque_prf_input)
423 gnutls_oprfi_enable_server (session, oprfi_callback, NULL);
424 #endif
426 return session;
429 #include <gnutls/x509.h>
431 static const char DEFAULT_DATA[] =
432 "This is the default message reported by the GnuTLS implementation. "
433 "For more information please visit "
434 "<a href=\"http://www.gnutls.org/\">http://www.gnutls.org/</a>.";
436 /* Creates html with the current session information.
438 #define tmp2 &http_buffer[strlen(http_buffer)]
439 static char *
440 peer_print_info (gnutls_session_t session, int *ret_length,
441 const char *header)
443 const char *tmp;
444 unsigned char sesid[32];
445 size_t i, sesid_size;
446 char *http_buffer;
447 gnutls_kx_algorithm_t kx_alg;
448 size_t len = 5 * 1024 + strlen (header);
449 char *crtinfo = NULL;
450 size_t ncrtinfo = 0;
452 if (verbose != 0)
454 http_buffer = malloc (len);
455 if (http_buffer == NULL)
456 return NULL;
458 strcpy (http_buffer, HTTP_BEGIN);
459 strcpy (&http_buffer[sizeof (HTTP_BEGIN) - 1], DEFAULT_DATA);
460 strcpy (&http_buffer[sizeof (HTTP_BEGIN) + sizeof (DEFAULT_DATA) - 2],
461 HTTP_END);
462 *ret_length =
463 sizeof (DEFAULT_DATA) + sizeof (HTTP_BEGIN) + sizeof (HTTP_END) - 3;
464 return http_buffer;
468 if (gnutls_certificate_type_get (session) == GNUTLS_CRT_X509)
470 const gnutls_datum_t *cert_list;
471 unsigned int cert_list_size = 0;
473 cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
475 for (i = 0; i < cert_list_size; i++)
477 gnutls_x509_crt_t cert;
478 gnutls_datum_t info;
480 if (gnutls_x509_crt_init (&cert) == 0 &&
481 gnutls_x509_crt_import (cert, &cert_list[i],
482 GNUTLS_X509_FMT_DER) == 0 &&
483 gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_FULL, &info) == 0)
485 const char *post = "</PRE><P><PRE>";
487 crtinfo = realloc (crtinfo, ncrtinfo + info.size +
488 strlen (post) + 1);
489 if (crtinfo == NULL)
490 return NULL;
491 memcpy (crtinfo + ncrtinfo, info.data, info.size);
492 ncrtinfo += info.size;
493 memcpy (crtinfo + ncrtinfo, post, strlen (post));
494 ncrtinfo += strlen (post);
495 crtinfo[ncrtinfo] = '\0';
496 gnutls_free (info.data);
501 http_buffer = malloc (len);
502 if (http_buffer == NULL)
503 return NULL;
505 strcpy (http_buffer, HTTP_BEGIN);
507 /* print session_id */
508 gnutls_session_get_id (session, sesid, &sesid_size);
509 sprintf (tmp2, "\n<p>Session ID: <i>");
510 for (i = 0; i < sesid_size; i++)
511 sprintf (tmp2, "%.2X", sesid[i]);
512 sprintf (tmp2, "</i></p>\n");
513 sprintf (tmp2,
514 "<h5>If your browser supports session resuming, then you should see the "
515 "same session ID, when you press the <b>reload</b> button.</h5>\n");
517 /* Here unlike print_info() we use the kx algorithm to distinguish
518 * the functions to call.
521 char dns[256];
522 size_t dns_size = sizeof (dns);
523 unsigned int type;
525 if (gnutls_server_name_get (session, dns, &dns_size, &type, 0) == 0)
527 sprintf (tmp2, "\n<p>Server Name: %s</p>\n", dns);
532 kx_alg = gnutls_kx_get (session);
534 /* print srp specific data */
535 #ifdef ENABLE_SRP
536 if (kx_alg == GNUTLS_KX_SRP)
538 sprintf (tmp2, "<p>Connected as user '%s'.</p>\n",
539 gnutls_srp_server_get_username (session));
541 #endif
543 #ifdef ENABLE_PSK
544 if (kx_alg == GNUTLS_KX_PSK)
546 sprintf (tmp2, "<p>Connected as user '%s'.</p>\n",
547 gnutls_psk_server_get_username (session));
549 #endif
551 #ifdef ENABLE_ANON
552 if (kx_alg == GNUTLS_KX_ANON_DH)
554 sprintf (tmp2,
555 "<p> Connect using anonymous DH (prime of %d bits)</p>\n",
556 gnutls_dh_get_prime_bits (session));
558 #endif
560 if (kx_alg == GNUTLS_KX_DHE_RSA || kx_alg == GNUTLS_KX_DHE_DSS)
562 sprintf (tmp2,
563 "Ephemeral DH using prime of <b>%d</b> bits.<br>\n",
564 gnutls_dh_get_prime_bits (session));
567 /* print session information */
568 strcat (http_buffer, "<P>\n");
570 tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
571 if (tmp == NULL)
572 tmp = str_unknown;
573 sprintf (tmp2,
574 "<TABLE border=1><TR><TD>Protocol version:</TD><TD>%s</TD></TR>\n",
575 tmp);
577 if (gnutls_auth_get_type (session) == GNUTLS_CRD_CERTIFICATE)
579 tmp =
580 gnutls_certificate_type_get_name (gnutls_certificate_type_get
581 (session));
582 if (tmp == NULL)
583 tmp = str_unknown;
584 sprintf (tmp2, "<TR><TD>Certificate Type:</TD><TD>%s</TD></TR>\n", tmp);
587 tmp = gnutls_kx_get_name (kx_alg);
588 if (tmp == NULL)
589 tmp = str_unknown;
590 sprintf (tmp2, "<TR><TD>Key Exchange:</TD><TD>%s</TD></TR>\n", tmp);
592 tmp = gnutls_compression_get_name (gnutls_compression_get (session));
593 if (tmp == NULL)
594 tmp = str_unknown;
595 sprintf (tmp2, "<TR><TD>Compression</TD><TD>%s</TD></TR>\n", tmp);
597 tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
598 if (tmp == NULL)
599 tmp = str_unknown;
600 sprintf (tmp2, "<TR><TD>Cipher</TD><TD>%s</TD></TR>\n", tmp);
602 tmp = gnutls_mac_get_name (gnutls_mac_get (session));
603 if (tmp == NULL)
604 tmp = str_unknown;
605 sprintf (tmp2, "<TR><TD>MAC</TD><TD>%s</TD></TR>\n", tmp);
607 tmp = gnutls_cipher_suite_get_name (kx_alg,
608 gnutls_cipher_get (session),
609 gnutls_mac_get (session));
610 if (tmp == NULL)
611 tmp = str_unknown;
612 sprintf (tmp2, "<TR><TD>Ciphersuite</TD><TD>%s</TD></TR></p></TABLE>\n",
613 tmp);
615 if (crtinfo)
617 strcat (http_buffer, "<hr><PRE>");
618 strcat (http_buffer, crtinfo);
619 strcat (http_buffer, "\n</PRE>\n");
622 strcat (http_buffer, "<hr><P>Your HTTP header was:<PRE>");
623 strcat (http_buffer, header);
624 strcat (http_buffer, "</PRE></P>");
626 strcat (http_buffer, "\n" HTTP_END);
628 *ret_length = strlen (http_buffer);
630 return http_buffer;
633 static const char *
634 human_addr (const struct sockaddr *sa, socklen_t salen,
635 char *buf, size_t buflen)
637 const char *save_buf = buf;
638 size_t l;
640 if (!buf || !buflen)
641 return NULL;
643 *buf = '\0';
645 switch (sa->sa_family)
647 #if HAVE_IPV6
648 case AF_INET6:
649 snprintf (buf, buflen, "IPv6 ");
650 break;
651 #endif
653 case AF_INET:
654 snprintf (buf, buflen, "IPv4 ");
655 break;
658 l = strlen (buf);
659 buf += l;
660 buflen -= l;
662 if (getnameinfo (sa, salen, buf, buflen, NULL, 0, NI_NUMERICHOST) != 0)
663 return NULL;
665 l = strlen (buf);
666 buf += l;
667 buflen -= l;
669 strncat (buf, " port ", buflen);
671 l = strlen (buf);
672 buf += l;
673 buflen -= l;
675 if (getnameinfo (sa, salen, NULL, 0, buf, buflen, NI_NUMERICSERV) != 0)
676 return NULL;
678 return save_buf;
681 static int
682 listen_socket (const char *name, int listen_port)
684 struct addrinfo hints, *res, *ptr;
685 char portname[6];
686 int s;
687 int yes;
688 listener_item *j = NULL;
690 snprintf (portname, sizeof (portname), "%d", listen_port);
691 memset (&hints, 0, sizeof (hints));
692 hints.ai_socktype = SOCK_STREAM;
693 hints.ai_flags = AI_PASSIVE;
695 if ((s = getaddrinfo (NULL, portname, &hints, &res)) != 0)
697 fprintf (stderr, "getaddrinfo() failed: %s\n", gai_strerror (s));
698 return -1;
700 s = -1;
702 for (ptr = res; ptr != NULL; ptr = ptr->ai_next)
704 /* Print what we are doing. */
706 char topbuf[512];
708 fprintf (stderr, "%s listening on %s...",
709 name, human_addr (ptr->ai_addr, ptr->ai_addrlen,
710 topbuf, sizeof (topbuf)));
713 if ((s = socket (ptr->ai_family, ptr->ai_socktype,
714 ptr->ai_protocol)) < 0)
716 perror ("socket() failed");
717 continue;
720 yes = 1;
721 if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
722 (const void *) &yes, sizeof (yes)) < 0)
724 perror ("setsockopt() failed");
725 failed:
726 close (s);
727 continue;
730 if (bind (s, ptr->ai_addr, ptr->ai_addrlen) < 0)
732 perror ("bind() failed");
733 goto failed;
736 if (listen (s, 10) < 0)
738 perror ("listen() failed");
739 goto failed;
742 /* new list entry for the connection */
743 lappend (listener_list);
744 j = listener_list.tail;
745 j->listen_socket = 1;
746 j->fd = s;
748 /* Complete earlier message. */
749 fprintf (stderr, "done\n");
752 fflush (stderr);
754 freeaddrinfo (res);
755 if (!j)
756 return -1;
758 return 0;
761 static void
762 get_response (gnutls_session_t session, char *request,
763 char **response, int *response_length)
765 char *p, *h;
767 if (http != 0)
769 if (strncmp (request, "GET ", 4))
770 goto unimplemented;
772 if (!(h = strchr (request, '\n')))
773 goto unimplemented;
775 *h++ = '\0';
776 while (*h == '\r' || *h == '\n')
777 h++;
779 if (!(p = strchr (request + 4, ' ')))
780 goto unimplemented;
781 *p = '\0';
783 /* *response = peer_print_info(session, request+4, h, response_length); */
784 if (http != 0)
786 *response = peer_print_info (session, response_length, h);
788 else
790 *response = strdup (request);
791 *response_length = ((*response) ? strlen (*response) : 0);
794 return;
796 unimplemented:
797 *response = strdup (HTTP_UNIMPLEMENTED);
798 *response_length = ((*response) ? strlen (*response) : 0);
801 static void terminate (int sig) __attribute__ ((noreturn));
803 static void
804 terminate (int sig)
806 fprintf (stderr, "Exiting via signal %d\n", sig);
807 exit (1);
811 static void
812 check_alert (gnutls_session_t session, int ret)
814 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
815 || ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
817 int last_alert = gnutls_alert_get (session);
818 if (last_alert == GNUTLS_A_NO_RENEGOTIATION &&
819 ret == GNUTLS_E_WARNING_ALERT_RECEIVED)
820 printf
821 ("* Received NO_RENEGOTIATION alert. Client does not support renegotiation.\n");
822 else
823 printf ("* Received alert '%d': %s.\n", last_alert,
824 gnutls_alert_get_name (last_alert));
828 static void
829 tls_log_func (int level, const char *str)
831 fprintf (stderr, "|<%d>| %s", level, str);
834 static void gaa_parser (int argc, char **argv);
837 main (int argc, char **argv)
839 int ret, n;
840 char topbuf[512];
841 char name[256];
842 int accept_fd;
843 struct sockaddr_storage client_address;
844 socklen_t calen;
846 set_program_name (argv[0]);
848 #ifdef gcry_fips_mode_active
849 if (gcry_fips_mode_active ())
851 ret = gnutls_register_md5_handler ();
852 if (ret)
853 fprintf (stderr, "gnutls_register_md5_handler: %s\n",
854 gnutls_strerror (ret));
856 #endif
858 #ifndef _WIN32
859 signal (SIGPIPE, SIG_IGN);
860 signal (SIGHUP, SIG_IGN);
861 signal (SIGTERM, terminate);
862 if (signal (SIGINT, terminate) == SIG_IGN)
863 signal (SIGINT, SIG_IGN); /* e.g. background process */
864 #endif
866 sockets_init ();
868 gaa_parser (argc, argv);
870 if (nodb == 0)
871 wrap_db_init ();
873 if (http == 1)
875 strcpy (name, "HTTP Server");
877 else
879 strcpy (name, "Echo Server");
882 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
884 if ((ret = gnutls_global_init ()) < 0)
886 fprintf (stderr, "global_init: %s\n", gnutls_strerror (ret));
887 exit (1);
890 if ((ret = gnutls_global_init_extra ()) < 0)
892 fprintf (stderr, "global_init_extra: %s\n", gnutls_strerror (ret));
893 exit (1);
896 gnutls_global_set_log_function (tls_log_func);
897 gnutls_global_set_log_level (debug);
899 /* Note that servers must generate parameters for
900 * Diffie-Hellman. See gnutls_dh_params_generate(), and
901 * gnutls_dh_params_set().
903 if (generate != 0)
905 generate_rsa_params ();
906 generate_dh_primes ();
908 else if (dh_params_file)
910 read_dh_params ();
912 else
914 static_dh_params ();
917 if (gnutls_certificate_allocate_credentials (&cert_cred) < 0)
919 fprintf (stderr, "memory error\n");
920 exit (1);
923 if (x509_cafile != NULL)
925 if ((ret = gnutls_certificate_set_x509_trust_file
926 (cert_cred, x509_cafile, x509ctype)) < 0)
928 fprintf (stderr, "Error reading '%s'\n", x509_cafile);
929 GERR (ret);
930 exit (1);
932 else
934 printf ("Processed %d CA certificate(s).\n", ret);
937 #ifdef ENABLE_PKI
938 if (x509_crlfile != NULL)
940 if ((ret = gnutls_certificate_set_x509_crl_file
941 (cert_cred, x509_crlfile, x509ctype)) < 0)
943 fprintf (stderr, "Error reading '%s'\n", x509_crlfile);
944 GERR (ret);
945 exit (1);
947 else
949 printf ("Processed %d CRL(s).\n", ret);
952 #endif
954 #ifdef ENABLE_OPENPGP
955 if (pgp_keyring != NULL)
957 ret =
958 gnutls_certificate_set_openpgp_keyring_file (cert_cred, pgp_keyring,
959 GNUTLS_OPENPGP_FMT_BASE64);
960 if (ret < 0)
962 fprintf (stderr, "Error setting the OpenPGP keyring file\n");
963 GERR (ret);
967 if (pgp_certfile != NULL)
969 if (info.pgp_subkey != NULL)
970 ret = gnutls_certificate_set_openpgp_key_file2
971 (cert_cred, pgp_certfile, pgp_keyfile, info.pgp_subkey,
972 GNUTLS_OPENPGP_FMT_BASE64);
973 else
974 ret = gnutls_certificate_set_openpgp_key_file
975 (cert_cred, pgp_certfile, pgp_keyfile, GNUTLS_OPENPGP_FMT_BASE64);
977 if (ret < 0)
979 fprintf (stderr,
980 "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
981 ret, pgp_certfile, pgp_keyfile);
982 GERR (ret);
985 #endif
987 if (x509_certfile != NULL)
988 if ((ret = gnutls_certificate_set_x509_key_file
989 (cert_cred, x509_certfile, x509_keyfile, x509ctype)) < 0)
991 fprintf (stderr,
992 "Error reading '%s' or '%s'\n", x509_certfile, x509_keyfile);
993 GERR (ret);
994 exit (1);
997 if (x509_dsacertfile != NULL)
998 if ((ret = gnutls_certificate_set_x509_key_file
999 (cert_cred, x509_dsacertfile, x509_dsakeyfile, x509ctype)) < 0)
1001 fprintf (stderr, "Error reading '%s' or '%s'\n",
1002 x509_dsacertfile, x509_dsakeyfile);
1003 GERR (ret);
1004 exit (1);
1007 gnutls_certificate_set_params_function (cert_cred, get_params);
1008 /* gnutls_certificate_set_dh_params(cert_cred, dh_params);
1009 * gnutls_certificate_set_rsa_export_params(cert_cred, rsa_params);
1012 /* this is a password file (created with the included srpcrypt utility)
1013 * Read README.crypt prior to using SRP.
1015 #ifdef ENABLE_SRP
1016 if (srp_passwd != NULL)
1018 gnutls_srp_allocate_server_credentials (&srp_cred);
1020 if ((ret =
1021 gnutls_srp_set_server_credentials_file (srp_cred, srp_passwd,
1022 srp_passwd_conf)) < 0)
1024 /* only exit is this function is not disabled
1026 fprintf (stderr, "Error while setting SRP parameters\n");
1027 GERR (ret);
1030 #endif
1032 /* this is a password file
1034 #ifdef ENABLE_PSK
1035 if (psk_passwd != NULL)
1037 gnutls_psk_allocate_server_credentials (&psk_cred);
1039 if ((ret =
1040 gnutls_psk_set_server_credentials_file (psk_cred, psk_passwd)) < 0)
1042 /* only exit is this function is not disabled
1044 fprintf (stderr, "Error while setting PSK parameters\n");
1045 GERR (ret);
1048 if (info.psk_hint)
1050 ret = gnutls_psk_set_server_credentials_hint (psk_cred,
1051 info.psk_hint);
1052 if (ret)
1054 fprintf (stderr, "Error setting PSK identity hint.\n");
1055 GERR (ret);
1059 gnutls_psk_set_server_params_function (psk_cred, get_params);
1061 #endif
1063 #ifdef ENABLE_ANON
1064 gnutls_anon_allocate_server_credentials (&dh_cred);
1065 gnutls_anon_set_server_params_function (dh_cred, get_params);
1067 /* gnutls_anon_set_server_dh_params(dh_cred, dh_params); */
1068 #endif
1070 #ifdef ENABLE_SESSION_TICKET
1071 if (noticket == 0)
1072 gnutls_session_ticket_key_generate (&session_ticket_key);
1073 #endif
1075 if (listen_socket (name, port) < 0)
1076 exit (1);
1078 for (;;)
1080 listener_item *j;
1081 fd_set rd, wr;
1082 int val;
1084 FD_ZERO (&rd);
1085 FD_ZERO (&wr);
1086 n = 0;
1088 /* flag which connections we are reading or writing to within the fd sets */
1089 lloopstart (listener_list, j)
1092 #ifndef _WIN32
1093 val = fcntl (j->fd, F_GETFL, 0);
1094 if ((val == -1) || (fcntl (j->fd, F_SETFL, val | O_NONBLOCK) < 0))
1096 perror ("fcntl()");
1097 exit (1);
1099 #endif
1101 if (j->listen_socket)
1103 FD_SET (j->fd, &rd);
1104 n = MAX (n, j->fd);
1106 if (j->http_state == HTTP_STATE_REQUEST)
1108 FD_SET (j->fd, &rd);
1109 n = MAX (n, j->fd);
1111 if (j->http_state == HTTP_STATE_RESPONSE)
1113 FD_SET (j->fd, &wr);
1114 n = MAX (n, j->fd);
1117 lloopend (listener_list, j);
1119 /* core operation */
1120 n = select (n + 1, &rd, &wr, NULL, NULL);
1121 if (n == -1 && errno == EINTR)
1122 continue;
1123 if (n < 0)
1125 perror ("select()");
1126 exit (1);
1129 /* read or write to each connection as indicated by select()'s return argument */
1130 lloopstart (listener_list, j)
1133 /* a new connection has arrived */
1134 if (FD_ISSET (j->fd, &rd) && j->listen_socket)
1136 gnutls_session_t tls_session;
1138 tls_session = initialize_session ();
1140 calen = sizeof (client_address);
1141 memset (&client_address, 0, calen);
1142 accept_fd = accept (j->fd, (struct sockaddr *) &client_address,
1143 &calen);
1145 if (accept_fd < 0)
1147 perror ("accept()");
1149 else
1151 time_t tt;
1152 char *ctt;
1154 /* new list entry for the connection */
1155 lappend (listener_list);
1156 j = listener_list.tail;
1157 j->http_request = (char *) strdup ("");
1158 j->http_state = HTTP_STATE_REQUEST;
1159 j->fd = accept_fd;
1161 j->tls_session = tls_session;
1162 gnutls_transport_set_ptr (tls_session,
1163 (gnutls_transport_ptr_t)
1164 gl_fd_to_handle (accept_fd));
1165 j->handshake_ok = 0;
1167 if (verbose == 0)
1169 tt = time (0);
1170 ctt = ctime (&tt);
1171 ctt[strlen (ctt) - 1] = 0;
1173 printf("\n* Accepted connection from %s on %s\n",
1174 human_addr ((struct sockaddr *)
1175 &client_address, calen, topbuf,
1176 sizeof (topbuf)), ctt);
1181 if (FD_ISSET (j->fd, &rd) && !j->listen_socket)
1183 /* read partial GET request */
1184 char buf[1024];
1185 int r, ret;
1187 if (j->handshake_ok == 0)
1189 r = gnutls_handshake (j->tls_session);
1190 if (r < 0 && gnutls_error_is_fatal (r) == 0)
1192 check_alert (j->tls_session, r);
1193 /* nothing */
1195 else if (r < 0 && gnutls_error_is_fatal (r) == 1)
1197 check_alert (j->tls_session, r);
1198 fprintf (stderr, "Error in handshake\n");
1199 GERR (r);
1203 ret =
1204 gnutls_alert_send_appropriate (j->tls_session, r);
1206 while (ret == GNUTLS_E_AGAIN);
1207 j->http_state = HTTP_STATE_CLOSING;
1209 else if (r == 0)
1211 if (gnutls_session_is_resumed (j->tls_session) != 0
1212 && verbose == 0)
1213 printf ("*** This is a resumed session\n");
1215 if (verbose == 0)
1217 printf ("\n* Successful handshake from %s\n",
1218 human_addr ((struct sockaddr *)
1219 &client_address, calen, topbuf,
1220 sizeof (topbuf)));
1221 print_info (j->tls_session, NULL, 1);
1223 j->handshake_ok = 1;
1227 if (j->handshake_ok == 1)
1229 r = gnutls_record_recv (j->tls_session, buf,
1230 MIN (1024, SMALL_READ_TEST));
1231 if (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN)
1233 /* do nothing */
1235 else if (r <= 0)
1237 j->http_state = HTTP_STATE_CLOSING;
1238 if (r < 0 && r != GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
1240 check_alert (j->tls_session, r);
1241 fprintf (stderr, "Error while receiving data\n");
1242 GERR (r);
1246 else
1248 j->http_request =
1249 realloc (j->http_request, j->request_length + r + 1);
1250 if (j->http_request != NULL)
1252 memcpy (j->http_request + j->request_length, buf, r);
1253 j->request_length += r;
1254 j->http_request[j->request_length] = '\0';
1256 else
1257 j->http_state = HTTP_STATE_CLOSING;
1260 /* check if we have a full HTTP header */
1262 j->http_response = NULL;
1263 if (j->http_request != NULL)
1265 if ((http == 0 && strchr (j->http_request, '\n'))
1266 || strstr (j->http_request, "\r\n\r\n")
1267 || strstr (j->http_request, "\n\n"))
1269 get_response (j->tls_session, j->http_request,
1270 &j->http_response, &j->response_length);
1271 j->http_state = HTTP_STATE_RESPONSE;
1272 j->response_written = 0;
1277 if (FD_ISSET (j->fd, &wr))
1279 /* write partial response request */
1280 int r;
1282 if (j->handshake_ok == 0)
1284 r = gnutls_handshake (j->tls_session);
1285 if (r < 0 && gnutls_error_is_fatal (r) == 0)
1287 check_alert (j->tls_session, r);
1288 /* nothing */
1290 else if (r < 0 && gnutls_error_is_fatal (r) == 1)
1292 int ret;
1294 j->http_state = HTTP_STATE_CLOSING;
1295 check_alert (j->tls_session, r);
1296 fprintf (stderr, "Error in handshake\n");
1297 GERR (r);
1301 ret =
1302 gnutls_alert_send_appropriate (j->tls_session, r);
1304 while (ret == GNUTLS_E_AGAIN);
1306 else if (r == 0)
1308 if (gnutls_session_is_resumed (j->tls_session) != 0
1309 && verbose == 0)
1310 printf ("*** This is a resumed session\n");
1311 if (verbose == 0)
1313 printf ("- connection from %s\n",
1314 human_addr ((struct sockaddr *)
1315 &client_address, calen, topbuf,
1316 sizeof (topbuf)));
1318 print_info (j->tls_session, NULL, 1);
1320 j->handshake_ok = 1;
1324 if (j->handshake_ok == 1)
1326 /* FIXME if j->http_response == NULL? */
1327 r = gnutls_record_send (j->tls_session,
1328 j->http_response +
1329 j->response_written,
1330 MIN (j->response_length -
1331 j->response_written,
1332 SMALL_READ_TEST));
1333 if (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN)
1335 /* do nothing */
1337 else if (r <= 0)
1339 if (http != 0)
1340 j->http_state = HTTP_STATE_CLOSING;
1341 else
1343 j->http_state = HTTP_STATE_REQUEST;
1344 free (j->http_response);
1345 j->response_length = 0;
1346 j->request_length = 0;
1347 j->http_request[0] = 0;
1350 if (r < 0)
1352 fprintf (stderr, "Error while sending data\n");
1353 GERR (r);
1355 check_alert (j->tls_session, r);
1357 else
1359 j->response_written += r;
1360 /* check if we have written a complete response */
1361 if (j->response_written == j->response_length)
1363 if (http != 0)
1364 j->http_state = HTTP_STATE_CLOSING;
1365 else
1367 j->http_state = HTTP_STATE_REQUEST;
1368 free (j->http_response);
1369 j->response_length = 0;
1370 j->request_length = 0;
1371 j->http_request[0] = 0;
1378 lloopend (listener_list, j);
1380 /* loop through all connections, closing those that are in error */
1381 lloopstart (listener_list, j)
1383 if (j->http_state == HTTP_STATE_CLOSING)
1385 ldeleteinc (listener_list, j);
1388 lloopend (listener_list, j);
1392 gnutls_certificate_free_credentials (cert_cred);
1394 #ifdef ENABLE_SRP
1395 if (srp_cred)
1396 gnutls_srp_free_server_credentials (srp_cred);
1397 #endif
1399 #ifdef ENABLE_PSK
1400 if (psk_cred)
1401 gnutls_psk_free_server_credentials (psk_cred);
1402 #endif
1404 #ifdef ENABLE_ANON
1405 gnutls_anon_free_server_credentials (dh_cred);
1406 #endif
1408 #ifdef ENABLE_SESSION_TICKET
1409 if (noticket == 0)
1410 gnutls_free (session_ticket_key.data);
1411 #endif
1413 if (nodb == 0)
1414 wrap_db_deinit ();
1415 gnutls_global_deinit ();
1417 return 0;
1421 void
1422 gaa_parser (int argc, char **argv)
1424 if (gaa (argc, argv, &info) != -1)
1426 fprintf (stderr,
1427 "Error in the arguments. Use the --help or -h parameters to get more information.\n");
1428 exit (1);
1431 disable_client_cert = info.disable_client_cert;
1432 require_cert = info.require_cert;
1433 debug = info.debug;
1434 verbose = info.quiet;
1435 nodb = info.nodb;
1436 noticket = info.noticket;
1438 if (info.http == 0)
1439 http = 0;
1440 else
1441 http = 1;
1443 if (info.fmtder == 0)
1444 x509ctype = GNUTLS_X509_FMT_PEM;
1445 else
1446 x509ctype = GNUTLS_X509_FMT_DER;
1448 if (info.generate == 0)
1449 generate = 0;
1450 else
1451 generate = 1;
1453 dh_params_file = info.dh_params_file;
1455 port = info.port;
1457 x509_certfile = info.x509_certfile;
1458 x509_keyfile = info.x509_keyfile;
1459 x509_dsacertfile = info.x509_dsacertfile;
1460 x509_dsakeyfile = info.x509_dsakeyfile;
1461 x509_cafile = info.x509_cafile;
1462 x509_crlfile = info.x509_crlfile;
1463 pgp_certfile = info.pgp_certfile;
1464 pgp_keyfile = info.pgp_keyfile;
1465 srp_passwd = info.srp_passwd;
1466 srp_passwd_conf = info.srp_passwd_conf;
1468 psk_passwd = info.psk_passwd;
1470 pgp_keyring = info.pgp_keyring;
1472 parse_protocols (info.proto, info.nproto, protocol_priority);
1473 parse_ciphers (info.ciphers, info.nciphers, cipher_priority);
1474 parse_macs (info.macs, info.nmacs, mac_priority);
1475 parse_ctypes (info.ctype, info.nctype, cert_type_priority);
1476 parse_kx (info.kx, info.nkx, kx_priority);
1477 parse_comp (info.comp, info.ncomp, comp_priority);
1480 extern void serv_version(void);
1482 void
1483 serv_version (void)
1485 const char *p = PACKAGE_NAME;
1486 if (strcmp (gnutls_check_version (NULL), PACKAGE_VERSION) != 0)
1487 p = PACKAGE_STRING;
1488 version_etc (stdout, program_name, p, gnutls_check_version (NULL),
1489 "Nikos Mavrogiannopoulos", (char *) NULL);
1492 /* session resuming support */
1494 #define SESSION_ID_SIZE 32
1495 #define SESSION_DATA_SIZE 1024
1497 typedef struct
1499 char session_id[SESSION_ID_SIZE];
1500 unsigned int session_id_size;
1502 char session_data[SESSION_DATA_SIZE];
1503 unsigned int session_data_size;
1504 } CACHE;
1506 static CACHE *cache_db;
1507 int cache_db_ptr = 0;
1509 static void
1510 wrap_db_init (void)
1512 /* allocate cache_db */
1513 cache_db = calloc (1, ssl_session_cache * sizeof (CACHE));
1516 static void
1517 wrap_db_deinit (void)
1521 static int
1522 wrap_db_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data)
1525 if (cache_db == NULL)
1526 return -1;
1528 if (key.size > SESSION_ID_SIZE)
1529 return -1;
1530 if (data.size > SESSION_DATA_SIZE)
1531 return -1;
1533 memcpy (cache_db[cache_db_ptr].session_id, key.data, key.size);
1534 cache_db[cache_db_ptr].session_id_size = key.size;
1536 memcpy (cache_db[cache_db_ptr].session_data, data.data, data.size);
1537 cache_db[cache_db_ptr].session_data_size = data.size;
1539 cache_db_ptr++;
1540 cache_db_ptr %= ssl_session_cache;
1542 return 0;
1545 static gnutls_datum_t
1546 wrap_db_fetch (void *dbf, gnutls_datum_t key)
1548 gnutls_datum_t res = { NULL, 0 };
1549 int i;
1551 if (cache_db == NULL)
1552 return res;
1554 for (i = 0; i < ssl_session_cache; i++)
1556 if (key.size == cache_db[i].session_id_size &&
1557 memcmp (key.data, cache_db[i].session_id, key.size) == 0)
1559 res.size = cache_db[i].session_data_size;
1561 res.data = gnutls_malloc (res.size);
1562 if (res.data == NULL)
1563 return res;
1565 memcpy (res.data, cache_db[i].session_data, res.size);
1567 return res;
1570 return res;
1573 static int
1574 wrap_db_delete (void *dbf, gnutls_datum_t key)
1576 int i;
1578 if (cache_db == NULL)
1579 return -1;
1581 for (i = 0; i < ssl_session_cache; i++)
1583 if (key.size == (unsigned int) cache_db[i].session_id_size &&
1584 memcmp (key.data, cache_db[i].session_id, key.size) == 0)
1587 cache_db[i].session_id_size = 0;
1588 cache_db[i].session_data_size = 0;
1590 return 0;
1594 return -1;