test: server can require client certificate in TLS handshake
[libisds.git] / test / simline / server.c
blobbcbbc697b2af172eeb0de6757bf6358409f25563
1 #ifndef _POSIX_SOURCE
2 #define _POSIX_SOURCE /* For getaddrinfo(3) */
3 #endif
5 #ifndef _BSD_SOURCE
6 #define _BSD_SOURCE /* For NI_MAXHOST */
7 #endif
9 #include "../test-tools.h"
10 #include "http.h"
11 #include "server.h"
12 #include "service.h"
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <errno.h> /* For EINTR */
19 #include <netdb.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <unistd.h>
23 #include <wait.h>
24 #include <gnutls/gnutls.h>
26 char *server_error = NULL;
28 static const char *as_path_hotp = "/as/processLogin?type=hotp&uri=";
29 static const char *as_path_sendsms = "/as/processLogin?type=totp&sendSms=true&uri=";
30 static const char *as_path_dontsendsms = "/as/processLogin?type=totp&uri=";
31 static const char *as_path_logout = "/as/processLogout?uri=";
32 static const char *asws_path = "/asws/changePassword";
33 static const char *ws_path = "/apps/DS/dz";
35 static const char *ws_base_path_basic = "/";
36 static const char *ws_base_path_otp = "/apps/";
38 static const char *authorization_cookie_name = "IPCZ-X-COOKIE";
39 static char *authorization_cookie_value = NULL;
41 /* TLS stuff */
42 static gnutls_certificate_credentials_t x509_credentials;
43 static gnutls_priority_t priority_cache;
44 static gnutls_dh_params_t dh_parameters;
46 /* Save error message if not yet set. The message will be duplicated.
47 * @message is printf(3) formatting string. */
48 void set_server_error(const char *message, ...) {
49 if (server_error == NULL) {
50 va_list ap;
51 va_start(ap, message);
52 test_vasprintf(&server_error, message, ap);
53 va_end(ap);
58 /* Creates listening TCP socket on localhost.
59 * Returns the socket descriptor or -1. */
60 int listen_on_socket(void) {
61 int retval;
62 struct addrinfo hints;
63 struct addrinfo *addresses, *address;
64 int fd;
66 memset(&hints, 0, sizeof(hints));
67 hints.ai_family = AF_UNSPEC;
68 hints.ai_socktype = SOCK_STREAM;
69 retval = getaddrinfo("localhost", NULL, &hints, &addresses);
70 if (retval) {
71 set_server_error("Could not resolve `localhost'");
72 return -1;
75 for (address = addresses; address != NULL; address = address->ai_next) {
76 fd = socket(address->ai_family, address->ai_socktype,
77 address->ai_protocol);
78 if (fd == -1) continue;
80 retval = bind(fd, address->ai_addr, address->ai_addrlen);
81 if (retval != 0) continue;
83 retval = listen(fd, 0);
84 if (retval == 0) {
85 freeaddrinfo(addresses);
86 return fd;
90 freeaddrinfo(addresses);
91 set_server_error("Could not start listening on TCP/localhost");
92 return -1;
96 /* Format socket address as printable string.
97 * @return allocated string or NULL in case of error. */
98 char *socket2address(int socket) {
99 struct sockaddr_storage storage;
100 socklen_t length = (socklen_t) sizeof(storage);
101 char host[NI_MAXHOST];
102 char service[NI_MAXSERV];
103 char *address = NULL;
105 if (-1 == getsockname(socket, (struct sockaddr *)&storage, &length)) {
106 set_server_error("Could not get address of server socket");
107 return NULL;
110 if (0 != getnameinfo((struct sockaddr *)&storage, length,
111 host, sizeof(host), service, sizeof(service),
112 NI_NUMERICHOST|NI_NUMERICSERV)) {
113 set_server_error("Could not resolve address of server socket");
114 return NULL;
117 if (-1 == test_asprintf(&address,
118 (strchr(host, ':') == NULL) ? "%s:%s" : "[%s]:%s",
119 host, service)) {
120 set_server_error("Could not format server address");
121 return NULL;
124 return address;
128 /* Process ISDS web service */
129 static void do_ws(const struct http_connection *connection,
130 const struct service_configuration *ws_configuration,
131 const struct http_request *request, const char *required_base_path) {
132 char *end_point = request->uri; /* Pointer to string in the request */
134 if (request->method != HTTP_METHOD_POST) {
135 http_send_response_400(connection,
136 "Regular ISDS web service request must be POST");
137 return;
140 if (required_base_path != NULL) {
141 size_t required_base_path_length = strlen(required_base_path);
142 if (strncmp(end_point, required_base_path, required_base_path_length)) {
143 http_send_response_400(connection, "Request sent to invalid path");
144 return;
146 end_point += required_base_path_length;
149 soap(connection, ws_configuration, request->body, request->body_length,
150 end_point);
154 /* Do the server protocol.
155 * @connection is HTTP connection
156 * @server_arguments is pointer to structure:
157 * @request is parsed HTTP client request
158 * @return 0 to accept new client, return -1 in case of fatal error. */
159 int server_basic_authentication(const struct http_connection *connection,
160 const void *server_arguments, const struct http_request *request) {
161 const struct arguments_basic_authentication *arguments =
162 (const struct arguments_basic_authentication *) server_arguments;
164 if (NULL == arguments || NULL == request) {
165 return -1;
168 if (request->method == HTTP_METHOD_POST) {
169 /* Only POST requests are used in Basic authentication mode */
170 if (arguments->username != NULL) {
171 if (http_client_authenticates(request)) {
172 switch(http_authenticate_basic(request,
173 arguments->username, arguments->password)) {
174 case HTTP_ERROR_SUCCESS:
175 do_ws(connection, arguments->services, request,
176 ws_base_path_basic);
177 break;
178 case HTTP_ERROR_CLIENT:
179 if (arguments->isds_deviations)
180 http_send_response_401_basic(connection);
181 else
182 http_send_response_403(connection);
183 break;
184 default:
185 http_send_response_500(connection,
186 "Server error while verifying Basic "
187 "authentication");
189 } else {
190 http_send_response_401_basic(connection);
192 } else {
193 do_ws(connection, arguments->services, request,
194 ws_base_path_basic);
196 } else {
197 /* HTTP method unsupported per ISDS specification */
198 http_send_response_400(connection,
199 "Only POST method is allowed");
202 return 0;
206 /* Process first phase of TOTP request */
207 static void do_as_sendsms(const struct http_connection *connection,
208 const struct http_request *request,
209 const struct arguments_otp_authentication *arguments) {
210 if (arguments == NULL) {
211 http_send_response_500(connection,
212 "Third argument of do_as_sendsms() is NULL");
213 return;
216 if (request->method != HTTP_METHOD_POST) {
217 http_send_response_400(connection,
218 "First phase TOTP request must be POST");
219 return;
222 if (!http_client_authenticates(request)) {
223 http_send_response_401_otp(connection,
224 "totpsendsms",
225 "authentication.error.userIsNotAuthenticated",
226 "Client did not send any authentication header");
227 return;
230 switch(http_authenticate_basic(request,
231 arguments->username, arguments->password)) {
232 case HTTP_ERROR_SUCCESS: {
233 /* Find final URI */
234 char *uri = strstr(request->uri, "&uri=");
235 if (uri == NULL) {
236 http_send_response_400(connection,
237 "Missing uri parameter in Request URI");
238 return;
240 uri += 5;
241 /* Build new location for second OTP phase */
242 char *location = NULL;
243 if (-1 == test_asprintf(&location, "%s%s", as_path_dontsendsms, uri)) {
244 http_send_response_500(connection,
245 "Could not build new localtion for "
246 "second OTP phase");
247 return;
249 char *terminator = strchr(uri, '&');
250 if (NULL != terminator)
251 location[strlen(as_path_dontsendsms) + (uri - terminator)] = '\0';
252 http_send_response_302_totp(connection,
253 "authentication.info.totpSended",
254 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
255 location);
256 free(location);
258 break;
259 case HTTP_ERROR_CLIENT:
260 if (arguments->isds_deviations)
261 http_send_response_401_otp(connection,
262 "totpsendsms",
263 "authentication.error.userIsNotAuthenticated",
264 " Retry: Bad user name or password in first OTP phase.\r\n"
265 " This is very long header\r\n"
266 " which should span to more lines.\r\n"
267 " Surrounding LWS are meaning-less. ");
268 else
269 http_send_response_403(connection);
270 break;
271 default:
272 http_send_response_500(connection,
273 "Could not verify Basic authentication");
278 /* Return static string representation of HTTP OTP authentication method.
279 * Or NULL in case of error. */
280 static const char *auth_otp_method2string(enum auth_otp_method method) {
281 switch (method) {
282 case AUTH_OTP_HMAC: return "hotp";
283 case AUTH_OTP_TIME: return "totp";
284 default: return NULL;
289 /* Process second phase of OTP request */
290 static void do_as_phase_two(const struct http_connection *connection,
291 const struct http_request *request,
292 const struct arguments_otp_authentication *arguments) {
293 if (arguments == NULL) {
294 http_send_response_500(connection,
295 "Third argument of do_as_phase_two() is NULL");
296 return;
299 if (request->method != HTTP_METHOD_POST) {
300 http_send_response_400(connection,
301 "Second phase OTP request must be POST");
302 return;
305 if (!http_client_authenticates(request)) {
306 http_send_response_401_otp(connection,
307 auth_otp_method2string(arguments->method),
308 "authentication.error.userIsNotAuthenticated",
309 "Client did not send any authentication header");
310 return;
313 switch(http_authenticate_otp(request,
314 arguments->username, arguments->password, arguments->otp)) {
315 case HTTP_ERROR_SUCCESS: {
316 /* Find final URI */
317 char *uri = strstr(request->uri, "&uri=");
318 if (uri == NULL) {
319 http_send_response_400(connection,
320 "Missing uri parameter in Request URI");
321 return;
323 uri += 5;
324 /* Build new location for final request */
325 char *location = NULL;
326 if (NULL == (location = strdup(uri))) {
327 http_send_response_500(connection,
328 "Could not build new location for final request");
329 return;
331 char *terminator = strchr(location, '&');
332 if (NULL != terminator)
333 *terminator = '\0';
334 /* Generate pseudo-random cookie value. This is to prevent
335 * client from reusing the cookie accidentally. We use the
336 * same seed to get reproducible tests. */
337 if (-1 == test_asprintf(&authorization_cookie_value, "%d",
338 rand())) {
339 http_send_response_500(connection,
340 "Could not generate cookie value");
341 free(location);
342 return;
344 /* XXX: Add Path parameter to cookie, otherwise
345 * different paths will not match.
346 * FIXME: Domain argument does not work with cURL. Report a bug. */
347 http_send_response_302_cookie(connection,
348 authorization_cookie_name,
349 authorization_cookie_value,
350 /*http_find_host(request)*/NULL,
351 /*NULL*/"/",
352 location);
353 free(location);
355 break;
356 case HTTP_ERROR_CLIENT:
357 if (arguments->isds_deviations)
358 http_send_response_401_otp(connection,
359 auth_otp_method2string(arguments->method),
360 "authentication.error.userIsNotAuthenticated",
361 " Retry: Bad user name or password in second OTP phase.\r\n"
362 " This is very long header\r\n"
363 " which should span to more lines.\r\n"
364 " Surrounding LWS are meaning-less. ");
365 else
366 http_send_response_403(connection);
367 break;
368 default:
369 http_send_response_500(connection,
370 "Could not verify OTP authentication");
375 /* Process ASWS for changing OTP password requests */
376 /* FIXME: The ASWS URI hosts two services: for sending TOTP code for password
377 * change and for changing OTP password. The problem is the former one is
378 * basic-authenticated, the later one is otp-authenticated. But we cannot
379 * decide which authentication to enforce without understadning request body.
380 * I will just try both of them to choose the service.
381 * But I hope official server implementation does it in more clever way. */
382 static void do_asws(const struct http_connection *connection,
383 const struct http_request *request,
384 const struct arguments_otp_authentication *arguments) {
385 http_error error;
386 if (arguments == NULL) {
387 http_send_response_500(connection,
388 "Third argument of do_asws() is NULL");
389 return;
392 if (request->method != HTTP_METHOD_POST) {
393 http_send_response_400(connection, "ASWS request must be POST");
394 return;
397 if (!http_client_authenticates(request)) {
398 http_send_response_401_otp(connection,
399 auth_otp_method2string(arguments->method),
400 "authentication.error.userIsNotAuthenticated",
401 "Client did not send any authentication header");
402 return;
405 /* Try OTP */
406 error = http_authenticate_otp(request,
407 arguments->username, arguments->password, arguments->otp);
408 if (HTTP_ERROR_SUCCESS == error) {
409 /* This will be request for password change because OTP
410 * authentication succeeded. */
411 do_ws(connection, arguments->services, request, NULL);
412 return;
415 if (AUTH_OTP_TIME == arguments->method) {
416 /* Try Basic */
417 error = http_authenticate_basic(request,
418 arguments->username, arguments->password);
419 if (HTTP_ERROR_SUCCESS == error) {
420 /* This will be request for time code for password change because
421 * basic authentication succeeded. */
422 do_ws(connection, arguments->services, request, NULL);
423 return;
427 if (HTTP_ERROR_CLIENT == error) {
428 if (arguments->isds_deviations)
429 http_send_response_401_otp(connection,
430 auth_otp_method2string(arguments->method),
431 "authentication.error.userIsNotAuthenticated",
432 " Retry: Bad user name or password in Authorization.\r\n"
433 " This is very long header\r\n"
434 " which should span to more lines.\r\n"
435 " Surrounding LWS are meaning-less. ");
436 else
437 http_send_response_403(connection);
438 return;
441 http_send_response_500(connection,
442 "Could not verify OTP authentication");
446 /* Process OTP session cookie invalidation request */
447 static void do_as_logout(const struct http_connection *connection,
448 const struct http_request *request,
449 const struct arguments_otp_authentication *arguments) {
450 if (arguments == NULL) {
451 http_send_response_500(connection,
452 "Third argument of do_as_logout() is NULL");
453 return;
456 if (request->method != HTTP_METHOD_GET) {
457 http_send_response_400(connection,
458 "OTP cookie invalidation request must be GET");
459 return;
462 const char *received_cookie =
463 http_find_cookie(request, authorization_cookie_name);
465 if (authorization_cookie_value == NULL || received_cookie == NULL ||
466 strcmp(authorization_cookie_value, received_cookie)) {
467 http_send_response_403(connection);
468 return;
471 /* XXX: Add Path parameter to cookie, otherwise
472 * different paths will not match.
473 * FIXME: Domain argument does not work with cURL. Report a bug. */
474 http_send_response_200_cookie(connection,
475 authorization_cookie_name,
477 /*http_find_host(request)*/ NULL,
478 /*NULL*/"/",
479 NULL, 0, NULL);
483 /* Process ISDS WS ping authorized by cookie */
484 static void do_ws_with_cookie(const struct http_connection *connection,
485 const struct http_request *request,
486 const struct arguments_otp_authentication *arguments,
487 const char *valid_base_path) {
488 const char *received_cookie =
489 http_find_cookie(request, authorization_cookie_name);
491 if (authorization_cookie_value != NULL && received_cookie != NULL &&
492 !strcmp(authorization_cookie_value, received_cookie))
493 do_ws(connection, arguments->services, request, valid_base_path);
494 else
495 http_send_response_403(connection);
499 /* Do the server protocol with OTP authentication.
500 * @connection is HTTP connection
501 * @server_arguments is pointer to structure arguments_otp_authentication. It
502 * selects OTP method to enable.
503 * @request is parsed HTTP client requrest
504 * @return 0 to accept new client, return -1 in case of fatal error. */
505 int server_otp_authentication(const struct http_connection *connection,
506 const void *server_arguments, const struct http_request *request) {
507 const struct arguments_otp_authentication *arguments =
508 (const struct arguments_otp_authentication *) server_arguments;
510 if (NULL == arguments || NULL == request) {
511 return(-1);
514 if (arguments->username != NULL) {
515 if (arguments->method == AUTH_OTP_HMAC &&
516 !strncmp(request->uri, as_path_hotp, strlen(as_path_hotp))) {
517 do_as_phase_two(connection, request, arguments);
518 } else if (arguments->method == AUTH_OTP_TIME &&
519 !strncmp(request->uri, as_path_sendsms,
520 strlen(as_path_sendsms))) {
521 do_as_sendsms(connection, request, arguments);
522 } else if (arguments->method == AUTH_OTP_TIME &&
523 !strncmp(request->uri, as_path_dontsendsms,
524 strlen(as_path_dontsendsms))) {
525 do_as_phase_two(connection, request, arguments);
526 } else if (!strncmp(request->uri, as_path_logout,
527 strlen(as_path_logout))) {
528 do_as_logout(connection, request, arguments);
529 } else if (!strcmp(request->uri, asws_path)) {
530 do_asws(connection, request, arguments);
531 } else if (!strcmp(request->uri, ws_path)) {
532 do_ws_with_cookie(connection, request, arguments,
533 ws_base_path_otp);
534 } else {
535 http_send_response_400(connection,
536 "Unknown path for OTP authenticating service");
538 } else {
539 if (!strcmp(request->uri, ws_path)) {
540 do_ws(connection, arguments->services, request,
541 ws_base_path_otp);
542 } else {
543 http_send_response_400(connection,
544 "Unknown path for OTP authenticating service");
548 return 0;
552 /* Implementation of server that is out of order.
553 * It always sends back SOAP Fault with HTTP error 503.
554 * @connection is HTTP connection
555 * @server_arguments is ununsed pointer
556 * @request is parsed HTTP client request
557 * @return 0 to accept new client, return -1 in case of fatal error. */
558 int server_out_of_order(const struct http_connection *connection,
559 const void *server_arguments, const struct http_request *request) {
560 const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
561 const char *fault = "<?xml version='1.0' encoding='UTF-8'?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode xsi:type=\"xsd:string\">Probíhá plánovaná údržba</faultcode><faultstring xsi:type=\"xsd:string\">Omlouváme se všem uživatelům datových schránek za dočasné omezení přístupu do systému datových schránek z důvodu plánované údržby systému. Děkujeme za pochopení.</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>";
563 http_send_response_503(connection, fault, strlen(fault),
564 soap_mime_type);
565 return 0;
569 /* Call-back for HTTP to receive data from socket
570 * API is equivalent to recv(2) except automatic interrupt handling. */
571 static ssize_t recv_plain(const struct http_connection *connection,
572 void *buffer, size_t length) {
573 ssize_t retval;
574 do {
575 retval = recv(connection->socket, buffer, length, 0);
576 } while (-1 == retval && EINTR == errno);
577 return retval;
581 /* Call-back for HTTP to sending data to socket
582 * API is equivalent to send(2) except automatic interrupt handling. */
583 static ssize_t send_plain(const struct http_connection *connection,
584 const void *buffer, size_t length) {
585 ssize_t retval;
586 do {
587 retval = send(connection->socket, buffer, length, MSG_NOSIGNAL);
588 } while (-1 == retval && EINTR == errno);
589 return retval;
593 /* Call-back for HTTP to receive data from TLS socket
594 * API is equivalent to recv(2) except automatic interrupt handling. */
595 static ssize_t recv_tls(const struct http_connection *connection,
596 void *buffer, size_t length) {
597 ssize_t retval;
598 do {
599 retval = gnutls_record_recv(connection->callback_data, buffer, length);
600 if (GNUTLS_E_REHANDSHAKE == retval) {
601 int error;
602 do {
603 error = gnutls_handshake(connection->callback_data);
604 } while (error < 0 && !gnutls_error_is_fatal(error));
605 if (error < 0) {
606 fprintf(stderr, "TLS rehandshake failed: %s\n",
607 gnutls_strerror(error));
608 return -1;
611 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
612 return (retval < 0) ? -1 : retval;
616 /* Call-back for HTTP to sending data to TLS socket
617 * API is equivalent to send(2) except automatic interrupt handling. */
618 static ssize_t send_tls(const struct http_connection *connection,
619 const void *buffer, size_t length) {
620 ssize_t retval;
621 do {
622 retval = gnutls_record_send(connection->callback_data, buffer, length);
623 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
624 return (retval < 0) ? -1 : retval;
628 /* Call-back fot GnuTLS to receive data from TCP socket.
629 * We override default implementation to unify passing http_connection as
630 * @context. Also otherwise there is need for ugly type cast from integer to
631 * pointer. */
632 static ssize_t tls_pull(gnutls_transport_ptr_t context, void* buffer,
633 size_t length) {
634 return recv( ((struct http_connection*)context)->socket,
635 buffer, length, 0);
639 /* Call-back fot GnuTLS to send data to TCP socket.
640 * GnuTLS does not call send(2) with MSG_NOSIGNAL, we must do it manually. */
641 static ssize_t tls_push(gnutls_transport_ptr_t context, const void* buffer,
642 size_t length) {
643 return send( ((struct http_connection*)context)->socket,
644 buffer, length, MSG_NOSIGNAL);
648 /* Start sever in separate process.
649 * @server_process is PID of forked server
650 * @server_address is automatically allocated TCP address of listening server
651 * @server_implementation points to kind of server to implement. Valid values
652 * are addresses of server_basic_authentication(),
653 * server_otp_authentication(), or server_out_of_order().
654 * @server_arguments is pointer to argument pass to @server_implementation. It
655 * usually contains:
656 * @username sets required user name server has to require. Set NULL to
657 * disable HTTP authentication.
658 * @password sets required password server has to require
659 * socket.
660 * @isds_deviations is flag to set conformance level. If false, server is
661 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
662 * specification. Otherwise server mimics real ISDS implementation as much
663 * as possible.
664 * @tls sets TLS layer. Pass NULL for plain HTTP.
665 * @return -1 in case of error. */
666 int start_server(pid_t *server_process, char **server_address,
667 int (*server_implementation)(const struct http_connection *,
668 const void *, const struct http_request *),
669 const void *server_arguments, const struct tls_authentication *tls) {
670 int server_socket;
671 int error;
673 if (server_address == NULL) {
674 set_server_error("start_server(): Got invalid server_address pointer");
675 return -1;
677 *server_address = NULL;
679 if (server_process == NULL) {
680 set_server_error("start_server(): Got invalid server_process pointer");
681 return -1;
684 server_socket = listen_on_socket();
685 if (server_socket == -1) {
686 set_server_error("Could not create listening socket");
687 return -1;
690 *server_address = socket2address(server_socket);
691 if (*server_address == NULL) {
692 close(server_socket);
693 set_server_error("Could not format address of listening address");
694 return -1;
697 if (NULL != tls && NULL == tls->server_certificate) {
698 /* XXX: X.509 TLS server requires server certificate. */
699 tls = NULL;
701 if (NULL != tls) {
702 const char *error_position;
703 if ((error = gnutls_global_init())) {
704 close(server_socket);
705 set_server_error("Could not initialize GnuTLS: %s",
706 gnutls_strerror(error));
707 return -1;
709 if ((error =
710 gnutls_certificate_allocate_credentials(&x509_credentials))) {
711 close(server_socket);
712 gnutls_global_deinit();
713 set_server_error("Could not allocate X.509 credentials: %s",
714 gnutls_strerror(error));
715 return -1;
717 if (NULL != tls->authority_certificate) {
718 if (0 > (error = gnutls_certificate_set_x509_trust_file(
719 x509_credentials, tls->authority_certificate,
720 GNUTLS_X509_FMT_PEM))) {
721 close(server_socket);
722 gnutls_certificate_free_credentials(x509_credentials);
723 gnutls_global_deinit();
724 set_server_error("Could not load authority certificate `%s': %s",
725 tls->authority_certificate, gnutls_strerror(error));
726 return -1;
729 if ((error = gnutls_certificate_set_x509_key_file(x509_credentials,
730 tls->server_certificate, tls->server_key,
731 GNUTLS_X509_FMT_PEM))) {
732 close(server_socket);
733 gnutls_certificate_free_credentials(x509_credentials);
734 gnutls_global_deinit();
735 set_server_error("Could not load server certificate or "
736 "private key `%s': %s", tls->server_certificate,
737 gnutls_strerror(error));
738 return -1;
740 if ((error = gnutls_priority_init(&priority_cache,
741 "PERFORMANCE", &error_position))) {
742 close(server_socket);
743 gnutls_certificate_free_credentials(x509_credentials);
744 gnutls_global_deinit();
745 if (error == GNUTLS_E_INVALID_REQUEST) {
746 set_server_error("Could not set TLS algorithm preferences: "
747 "%s Error at `%s'.",
748 gnutls_strerror(error), error_position);
749 set_server_error("Could not set TLS algorithm preferences: %s",
750 gnutls_strerror(error));
752 return -1;
754 /* XXX: priority_cache is linked from x509_credentials now.
755 * Deinitialization must free x509_credentials before priority_cache. */
757 if ((error = gnutls_dh_params_init(&dh_parameters))) {
758 close(server_socket);
759 gnutls_certificate_free_credentials(x509_credentials);
760 gnutls_priority_deinit(priority_cache);
761 gnutls_global_deinit();
762 set_server_error("Could not allocate Diffie-Hellman parameters: "
763 "%s", gnutls_strerror(error));
764 return -1;
766 if ((error = gnutls_dh_params_generate2(dh_parameters,
767 gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
768 GNUTLS_SEC_PARAM_LOW)))) {
769 close(server_socket);
770 gnutls_certificate_free_credentials(x509_credentials);
771 gnutls_priority_deinit(priority_cache);
772 gnutls_dh_params_deinit(dh_parameters);
773 gnutls_global_deinit();
774 set_server_error("Could not generate Diffie-Hellman parameters: %s",
775 gnutls_strerror(error));
776 return -1;
778 gnutls_certificate_set_dh_params(x509_credentials, dh_parameters);
779 /* XXX: dh_parameters are linked from x509_credentials now.
780 * Deinitialization must free x509_credentials before dh_parameters. */
783 *server_process = fork();
784 if (*server_process == -1) {
785 close(server_socket);
786 if (NULL != tls) {
787 gnutls_certificate_free_credentials(x509_credentials);
788 gnutls_priority_deinit(priority_cache);
789 gnutls_dh_params_deinit(dh_parameters);
790 gnutls_global_deinit();
792 set_server_error("Server could not been forked");
793 return -1;
796 if (*server_process == 0) {
797 int client_socket;
798 gnutls_session_t tls_session;
799 struct http_connection connection;
800 struct http_request *request = NULL;
801 http_error error;
802 int terminate = 0;
804 while (!terminate) {
805 if (NULL != tls) {
806 if ((error = gnutls_init(&tls_session, GNUTLS_SERVER))) {
807 set_server_error("Could not initialize TLS session: %s",
808 gnutls_strerror(error));
809 terminate = -1;
810 continue;
812 if ((error = gnutls_priority_set(tls_session,
813 priority_cache))) {
814 set_server_error(
815 "Could not set priorities to TLS session: %s",
816 gnutls_strerror(error));
817 terminate = -1;
818 gnutls_deinit(tls_session);
819 continue;
821 if ((error = gnutls_credentials_set(tls_session,
822 GNUTLS_CRD_CERTIFICATE, x509_credentials))) {
823 set_server_error("Could not set X509 credentials to TLS "
824 "session: %s", gnutls_strerror(error));
825 terminate = -1;
826 gnutls_deinit(tls_session);
827 continue;
829 /* XXX: Credentials are linked from session now.
830 * Deinitializition muse free session before x509_credentials.
832 if (NULL != tls->client_name) {
833 gnutls_certificate_server_set_request(tls_session,
834 GNUTLS_CERT_REQUIRE);
835 /* TODO: Register peer verification routine:
836 void gnutls_certificate_set_verify_function(
837 gnutls_certificate_credentials_t CRED,
838 gnutls_certificate_verify_function *FUNC);
839 Call-back is: int (*FUNC)(gnutls_session_t);
840 Call-back can use: gnutls_certificate_verify_peers2() ,
841 gnutls_certificate_type_get(),
842 gnutls_certificate_get_peers()
843 Return value is 0 for acception, other value for denial.
848 if (0 > (client_socket = accept(server_socket, NULL, NULL))) {
849 terminate = -1;
850 if (NULL != tls)
851 gnutls_deinit(tls_session);
852 continue;
854 fprintf(stderr, "Connection accepted\n");
855 connection.socket = client_socket;
857 if (NULL == tls) {
858 connection.recv_callback = recv_plain;
859 connection.send_callback = send_plain;
860 connection.callback_data = NULL;
861 } else {
862 connection.recv_callback = recv_tls;
863 connection.send_callback = send_tls;
864 connection.callback_data = tls_session;
865 gnutls_transport_set_pull_function(tls_session, tls_pull);
866 gnutls_transport_set_push_function(tls_session, tls_push);
867 gnutls_transport_set_ptr2(tls_session,
868 &connection, &connection);
869 do {
870 error = gnutls_handshake(tls_session);
871 } while (error < 0 && !gnutls_error_is_fatal(error));
872 if (error < 0) {
873 fprintf(stderr, "TLS handshake failed: %s\n",
874 gnutls_strerror(error));
875 close(client_socket);
876 gnutls_deinit(tls_session);
877 continue;
881 error = http_read_request(&connection, &request);
882 if (error) {
883 fprintf(stderr, "Error while reading request\n");
884 if (error == HTTP_ERROR_CLIENT)
885 http_send_response_400(&connection, "Error in request");
886 else
887 http_send_response_500(&connection,
888 "Could not read request");
889 close(client_socket);
890 if (NULL != tls)
891 gnutls_deinit(tls_session);
892 continue;
895 terminate = server_implementation(&connection, server_arguments,
896 request);
898 http_request_free(&request);
899 close(client_socket);
900 if (NULL != tls) {
901 gnutls_deinit(tls_session);
905 close(server_socket);
906 free(authorization_cookie_value);
907 exit(EXIT_SUCCESS);
908 /* Does not return */
911 return 0;
915 /* Kill the server process.
916 * Return 0. Return -1 if server could not been stopped. Return 1 if server
917 * crashed. */
918 int stop_server(pid_t server_process) {
919 int status;
920 if (server_process <= 0) {
921 set_server_error("Invalid server PID to kill");
922 return -1;
924 if (-1 == kill(server_process, SIGTERM)) {
925 set_server_error("Could not terminate server");
926 return -1;
928 if (-1 == waitpid(server_process, &status, 0)) {
929 set_server_error("Could not wait for server termination");
930 return -1;
932 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
933 set_server_error("Server terminated by signal %d violently",
934 WTERMSIG(status));
935 return 1;
937 return 0;