aboutsummaryrefslogtreecommitdiff
path: root/src/examples/https_server_example.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/examples/https_server_example.c')
-rw-r--r--src/examples/https_server_example.c261
1 files changed, 194 insertions, 67 deletions
diff --git a/src/examples/https_server_example.c b/src/examples/https_server_example.c
index 78f7176b..459b4aa2 100644
--- a/src/examples/https_server_example.c
+++ b/src/examples/https_server_example.c
@@ -20,7 +20,17 @@
20 20
21/** 21/**
22 * @file https_server_example.c 22 * @file https_server_example.c
23 * @brief a simple echo server using TLS. echo input from client until 'exit' message is received. 23 * @brief a simple https file server using TLS.
24 *
25 * This example assumes the existence of a private key file named "key.pem"
26 * and a server certificate file named "cert.pem". File path for these should be
27 * provided as command-line arguments. 'certtool' may be used to generate these if
28 * missing.
29 *
30 * Access server with your browser of choice or with curl :
31 *
32 * curl --insecure --tlsv1 --ciphers AES256-SHA <url>
33 *
24 * @author LV-426 34 * @author LV-426
25 */ 35 */
26 36
@@ -35,64 +45,76 @@
35#include <string.h> 45#include <string.h>
36#include <stdio.h> 46#include <stdio.h>
37#include <gnutls/gnutls.h> 47#include <gnutls/gnutls.h>
48#include <gcrypt.h>
38 49
39#define DH_BITS 1024 50#define BUF_SIZE 1024
40#define MAX_BUF 1024 51#define MAX_URL_LEN 255
41/* server credintials */
42gnutls_anon_server_credentials_t anoncred;
43 52
44/* server Diffie-Hellman parameters */ 53#define KEYFILE "key.pem"
45static gnutls_dh_params_t dh_params; 54#define CERTFILE "cert.pem"
46 55
56// TODO remove if unused
57#define CAFILE "ca.pem"
58#define CRLFILE "crl.pem"
47 59
48/* Generate Diffie Hellman parameters - for use with DHE kx algorithms. */ 60#define PAGE_NOT_FOUND "<html><head><title>File not found</title></head><body>File not found</body></html>"
49static int
50generate_dh_params (void)
51{
52
53 gnutls_dh_params_init (&dh_params);
54 gnutls_dh_params_generate2 (dh_params, DH_BITS);
55 return 0;
56}
57 61
58gnutls_session_t 62gnutls_session_t
59initialize_tls_session (void) 63initialize_tls_session (struct MHD_Connection *connection)
60{ 64{
61 gnutls_session_t session; 65 gnutls_session_t session;
62 66
63 gnutls_init (&session, GNUTLS_SERVER); 67 gnutls_init (&session, GNUTLS_SERVER);
64 68
65 gnutls_priority_set_direct (session, "NORMAL:+ANON-DH", NULL); 69 /* sets cipher priorities */
70 gnutls_priority_set (session, connection->daemon->priority_cache);
66 71
67 gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred); 72 /* set needed credentials for certificate authentication. */
68 73 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE,
69 gnutls_dh_set_prime_bits (session, DH_BITS); 74 connection->daemon->x509_cret);
70 75
71 return session; 76 return session;
72} 77}
73 78
74/* Accept Policy Callback */
75static int 79static int
76TLS_echo (void *cls, 80file_reader (void *cls, size_t pos, char *buf, int max)
77 struct MHD_Connection *connection,
78 const char *url,
79 const char *method,
80 const char *upload_data,
81 const char *version, unsigned int *upload_data_size, void **ptr)
82{ 81{
82 FILE *file = cls;
83
84 fseek (file, pos, SEEK_SET);
85 return fread (buf, 1, max, file);
86}
87
88/* HTTPS access handler call back */
89static int
90https_ahc (void *cls,
91 struct MHD_Connection *connection,
92 const char *url,
93 const char *method,
94 const char *upload_data,
95 const char *version, unsigned int *upload_data_size, void **ptr)
96{
97 /* loopback HTTP socket */
98 int loopback_sd, err;
99 ssize_t ret;
100 struct sockaddr_in servaddr4;
101 const struct sockaddr *servaddr;
102 struct sockaddr_in loopback_sa;
103 socklen_t addrlen;
104
83 gnutls_session_t session; 105 gnutls_session_t session;
84 static int aptr; 106 static int aptr;
85 struct MHD_Response *response; 107 struct MHD_Response *response;
86 char buffer[MAX_BUF + 1]; 108 char buffer[BUF_SIZE];
87 int ret;
88 109
89 printf ("accepted connection from %d\n", connection->addr->sin_addr); 110 printf ("accepted connection from %d\n", connection->addr->sin_addr);
90 111
91 session = initialize_tls_session (); 112 session = initialize_tls_session (connection);
92 113
93 gnutls_transport_set_ptr (session, connection->socket_fd); 114 gnutls_transport_set_ptr (session, connection->socket_fd);
94 115
95 ret = gnutls_handshake (session); 116 ret = gnutls_handshake (session);
117
96 if (ret < 0) 118 if (ret < 0)
97 { 119 {
98 /* set connection as closed */ 120 /* set connection as closed */
@@ -106,77 +128,182 @@ TLS_echo (void *cls,
106 printf ("TLS Handshake completed\n"); 128 printf ("TLS Handshake completed\n");
107 connection->state = MHDS_HANDSHAKE_COMPLETE; 129 connection->state = MHDS_HANDSHAKE_COMPLETE;
108 130
109 /* simple echo loop. message encryption/decryption is acheived through 'gnutls_record_send' 131 /* initialize loopback socket */
110 * & gnutls_record_recv calls. */ 132 loopback_sd = socket (AF_INET, SOCK_STREAM, 0);
133 memset (&loopback_sa, '\0', sizeof (loopback_sa));
134 loopback_sa.sin_family = AF_INET;
135
136 // TODO solve magic number issue - the http's daemons port must be shared with the https daemon - rosolve data sharing point
137 loopback_sa.sin_port = htons (50000);
138 inet_pton (AF_INET, "127.0.0.1", &loopback_sa.sin_addr);
139
140 /* connect loopback socket */
141 err = connect (loopback_sd, (struct sockaddr *) &loopback_sa,
142 sizeof (loopback_sa));
143 if (err < 0)
144 {
145 // TODO err handle
146 fprintf (stderr, "Error : failed to create TLS loopback socket\n");
147 exit (1);
148 }
149
150 /*
151 * This loop pipes data received through the TLS tunnel into the loopback connection.
152 * message encryption/decryption is acheived via 'gnutls_record_send' & gnutls_record_recv calls.
153 */
154 memset (buffer, 0, BUF_SIZE);
155 if (gnutls_record_recv (session, buffer, BUF_SIZE) < 0)
156 {
157 fprintf (stderr, "\n*** Received corrupted "
158 "data(%d). Closing the connection.\n\n", ret);
159 connection->socket_fd = -1;
160 gnutls_deinit (session);
161 return MHD_NO;
162 }
163
164 if (write (loopback_sd, buffer, BUF_SIZE) < 0)
165 {
166 printf ("failed to write to TLS loopback socket\n");
167 connection->socket_fd = -1;
168 gnutls_deinit (session);
169 return MHD_NO;
170 }
171
111 for (;;) 172 for (;;)
112 { 173 {
113 memset (buffer, 0, MAX_BUF + 1); 174 memset (buffer, 0, BUF_SIZE);
114 ret = gnutls_record_recv (session, buffer, MAX_BUF); 175
176 ret = read (loopback_sd, buffer, BUF_SIZE);
115 177
116 if (ret < 0) 178 if (ret < 0)
117 { 179 {
118 fprintf (stderr, "\n*** Received corrupted " 180 printf ("failed to read from TLS loopback socket\n");
119 "data(%d). Closing the connection.\n\n", ret);
120 break; 181 break;
121 } 182 }
122 else if (ret >= 0) 183
184 if (ret == 0)
123 { 185 {
124 if (strcmp (buffer, "exit") == 0) 186 break;
125 {
126 printf ("\n- Peer has closed the GNUTLS connection\n");
127 break;
128 }
129 else
130 {
131 /* echo data back to the client */
132 gnutls_record_send (session, buffer, strlen (buffer));
133 }
134 } 187 }
135 }
136 printf ("\n");
137 188
189 /* echo data back to the client */
190 ret = gnutls_record_send (session, buffer, ret);
191 if (ret < 0)
192 {
193 printf ("failed to write to TLS socket\n");
194 break;
195 }
196 }
138 /* mark connection as closed */ 197 /* mark connection as closed */
139 connection->socket_fd = -1; 198 connection->socket_fd = -1;
140
141 gnutls_deinit (session); 199 gnutls_deinit (session);
142 200
201 return MHD_YES;
202}
203
204/* HTTP access handler call back */
205static int
206http_ahc (void *cls,
207 struct MHD_Connection *connection,
208 const char *url,
209 const char *method,
210 const char *upload_data,
211 const char *version, unsigned int *upload_data_size, void **ptr)
212{
213 static int aptr;
214 static char full_url[MAX_URL_LEN];
215 struct MHD_Response *response;
216 int ret;
217 FILE *file;
218 struct stat buf;
219
220 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
221 return MHD_NO; /* unexpected method */
222 if (&aptr != *ptr)
223 {
224 /* do never respond on first call */
225 *ptr = &aptr;
226 return MHD_YES;
227 }
228 *ptr = NULL; /* reset when done */
229
230 /* assemble full url */
231 strcpy (full_url, connection->daemon->doc_root);
232 strncat (full_url, url,
233 MAX_URL_LEN - strlen (connection->daemon->doc_root) - 1);
234
235 file = fopen (full_url, "r");
236 if (file == NULL)
237 {
238 response = MHD_create_response_from_data (strlen (PAGE_NOT_FOUND),
239 (void *) PAGE_NOT_FOUND,
240 MHD_NO, MHD_NO);
241 ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, response);
242 MHD_destroy_response (response);
243 }
244 else
245 {
246 stat (&url[1], &buf);
247 response = MHD_create_response_from_callback (buf.st_size, 32 * 1024, /* 32k PAGE_NOT_FOUND size */
248 &file_reader, file,
249 (MHD_ContentReaderFreeCallback)
250 & fclose);
251 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
252 MHD_destroy_response (response);
253 }
143 return ret; 254 return ret;
144} 255}
145 256
146int 257int
147main (int argc, char *const *argv) 258main (int argc, char *const *argv)
148{ 259{
149 struct MHD_Daemon *daemon; 260 char keyfile[255] = KEYFILE;
261 char certfile[255] = CERTFILE;
262 struct MHD_Daemon *HTTP_daemon;
150 struct MHD_Daemon *TLS_daemon; 263 struct MHD_Daemon *TLS_daemon;
151 264
152 /* look for HTTPS port argument */ 265 /* look for HTTPS arguments */
153 if (argc < 4) 266 if (argc < 5)
154 { 267 {
155 printf ("Usage : %s HTTP-PORT SECONDS-TO-RUN HTTPS-PORT\n", argv[0]); 268 printf
269 ("Usage : %s HTTP-PORT SECONDS-TO-RUN HTTPS-PORT X.509_FILE_PATH\n",
270 argv[0]);
156 return 1; 271 return 1;
157 } 272 }
158 273
159 gnutls_global_init (); 274 // TODO check if this is truly necessary - disallow usage of the blocking /dev/random */
160 275 // gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0);
161 gnutls_anon_allocate_server_credentials (&anoncred);
162 276
163 generate_dh_params (); 277 HTTP_daemon =
278 MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
279 atoi (argv[1]), NULL, NULL, &http_ahc, MHD_OPTION_END);
164 280
165 gnutls_anon_set_server_dh_params (anoncred, dh_params); 281 if (HTTP_daemon == NULL)
282 {
283 printf ("Error: failed to start HTTP_daemon");
284 return 1;
285 }
166 286
167 TLS_daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 287 TLS_daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG
168 | MHD_USE_DEBUG | MHD_USE_SSL, 288 | MHD_USE_SSL, atoi (argv[3]),
169 atoi (argv[3]), NULL, NULL, &TLS_echo, NULL, 289 NULL,
290 NULL, &https_ahc,
291 NULL, MHD_OPTION_CONNECTION_TIMEOUT, 256,
292 MHD_OPTION_HTTPS_KEY_PATH, argv[4],
293 MHD_OPTION_HTTPS_CERT_PATH, argv[4],
170 MHD_OPTION_END); 294 MHD_OPTION_END);
171 295
172 if (TLS_daemon == NULL) 296 if (TLS_daemon == NULL)
173 return 1; 297 {
298 printf ("Error: failed to start TLS_daemon");
299 return 1;
300 }
301
174 sleep (atoi (argv[2])); 302 sleep (atoi (argv[2]));
175 303
176 MHD_stop_daemon (daemon); 304 MHD_stop_daemon (HTTP_daemon);
177 305
178 gnutls_anon_free_server_credentials (anoncred); 306 MHD_stop_daemon (TLS_daemon);
179 307
180 gnutls_global_deinit ();
181 return 0; 308 return 0;
182} 309}