diff options
Diffstat (limited to 'src/examples/https_server_example.c')
-rw-r--r-- | src/examples/https_server_example.c | 261 |
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 */ | ||
42 | gnutls_anon_server_credentials_t anoncred; | ||
43 | 52 | ||
44 | /* server Diffie-Hellman parameters */ | 53 | #define KEYFILE "key.pem" |
45 | static 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>" |
49 | static int | ||
50 | generate_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 | ||
58 | gnutls_session_t | 62 | gnutls_session_t |
59 | initialize_tls_session (void) | 63 | initialize_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 */ | ||
75 | static int | 79 | static int |
76 | TLS_echo (void *cls, | 80 | file_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 */ | ||
89 | static int | ||
90 | https_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 */ | ||
205 | static int | ||
206 | http_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 | ||
146 | int | 257 | int |
147 | main (int argc, char *const *argv) | 258 | main (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 | } |