test: server: Validate client's certificate
[libisds.git] / test / simline / server.c
blobab8ee4a34ad7df47b652c2a4833b7b023501a681
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>
25 #include <gnutls/x509.h>
27 char *server_error = NULL;
29 static const char *as_path_hotp = "/as/processLogin?type=hotp&uri=";
30 static const char *as_path_sendsms = "/as/processLogin?type=totp&sendSms=true&uri=";
31 static const char *as_path_dontsendsms = "/as/processLogin?type=totp&uri=";
32 static const char *as_path_logout = "/as/processLogout?uri=";
33 static const char *asws_path = "/asws/changePassword";
34 static const char *ws_path = "/apps/DS/dz";
36 static const char *ws_base_path_basic = "/";
37 static const char *ws_base_path_otp = "/apps/";
39 static const char *authorization_cookie_name = "IPCZ-X-COOKIE";
40 static char *authorization_cookie_value = NULL;
42 /* TLS stuff */
43 static gnutls_certificate_credentials_t x509_credentials;
44 static gnutls_priority_t priority_cache;
45 static gnutls_dh_params_t dh_parameters;
47 /* Save error message if not yet set. The message will be duplicated.
48 * @message is printf(3) formatting string. */
49 void set_server_error(const char *message, ...) {
50 if (server_error == NULL) {
51 va_list ap;
52 va_start(ap, message);
53 test_vasprintf(&server_error, message, ap);
54 va_end(ap);
59 /* Creates listening TCP socket on localhost.
60 * Returns the socket descriptor or -1. */
61 int listen_on_socket(void) {
62 int retval;
63 struct addrinfo hints;
64 struct addrinfo *addresses, *address;
65 int fd;
67 memset(&hints, 0, sizeof(hints));
68 hints.ai_family = AF_UNSPEC;
69 hints.ai_socktype = SOCK_STREAM;
70 retval = getaddrinfo("localhost", NULL, &hints, &addresses);
71 if (retval) {
72 set_server_error("Could not resolve `localhost'");
73 return -1;
76 for (address = addresses; address != NULL; address = address->ai_next) {
77 fd = socket(address->ai_family, address->ai_socktype,
78 address->ai_protocol);
79 if (fd == -1) continue;
81 retval = bind(fd, address->ai_addr, address->ai_addrlen);
82 if (retval != 0) continue;
84 retval = listen(fd, 0);
85 if (retval == 0) {
86 freeaddrinfo(addresses);
87 return fd;
91 freeaddrinfo(addresses);
92 set_server_error("Could not start listening on TCP/localhost");
93 return -1;
97 /* Format socket address as printable string.
98 * @return allocated string or NULL in case of error. */
99 char *socket2address(int socket) {
100 struct sockaddr_storage storage;
101 socklen_t length = (socklen_t) sizeof(storage);
102 char host[NI_MAXHOST];
103 char service[NI_MAXSERV];
104 char *address = NULL;
106 if (-1 == getsockname(socket, (struct sockaddr *)&storage, &length)) {
107 set_server_error("Could not get address of server socket");
108 return NULL;
111 if (0 != getnameinfo((struct sockaddr *)&storage, length,
112 host, sizeof(host), service, sizeof(service),
113 NI_NUMERICHOST|NI_NUMERICSERV)) {
114 set_server_error("Could not resolve address of server socket");
115 return NULL;
118 if (-1 == test_asprintf(&address,
119 (strchr(host, ':') == NULL) ? "%s:%s" : "[%s]:%s",
120 host, service)) {
121 set_server_error("Could not format server address");
122 return NULL;
125 return address;
129 /* Process ISDS web service */
130 static void do_ws(const struct http_connection *connection,
131 const struct service_configuration *ws_configuration,
132 const struct http_request *request, const char *required_base_path) {
133 char *end_point = request->uri; /* Pointer to string in the request */
135 if (request->method != HTTP_METHOD_POST) {
136 http_send_response_400(connection,
137 "Regular ISDS web service request must be POST");
138 return;
141 if (required_base_path != NULL) {
142 size_t required_base_path_length = strlen(required_base_path);
143 if (strncmp(end_point, required_base_path, required_base_path_length)) {
144 http_send_response_400(connection, "Request sent to invalid path");
145 return;
147 end_point += required_base_path_length;
150 soap(connection, ws_configuration, request->body, request->body_length,
151 end_point);
155 /* Do the server protocol.
156 * @connection is HTTP connection
157 * @server_arguments is pointer to structure:
158 * @request is parsed HTTP client request
159 * @return 0 to accept new client, return -1 in case of fatal error. */
160 int server_basic_authentication(const struct http_connection *connection,
161 const void *server_arguments, const struct http_request *request) {
162 const struct arguments_basic_authentication *arguments =
163 (const struct arguments_basic_authentication *) server_arguments;
165 if (NULL == arguments || NULL == request) {
166 return -1;
169 if (request->method == HTTP_METHOD_POST) {
170 /* Only POST requests are used in Basic authentication mode */
171 if (arguments->username != NULL) {
172 if (http_client_authenticates(request)) {
173 switch(http_authenticate_basic(request,
174 arguments->username, arguments->password)) {
175 case HTTP_ERROR_SUCCESS:
176 do_ws(connection, arguments->services, request,
177 ws_base_path_basic);
178 break;
179 case HTTP_ERROR_CLIENT:
180 if (arguments->isds_deviations)
181 http_send_response_401_basic(connection);
182 else
183 http_send_response_403(connection);
184 break;
185 default:
186 http_send_response_500(connection,
187 "Server error while verifying Basic "
188 "authentication");
190 } else {
191 http_send_response_401_basic(connection);
193 } else {
194 do_ws(connection, arguments->services, request,
195 ws_base_path_basic);
197 } else {
198 /* HTTP method unsupported per ISDS specification */
199 http_send_response_400(connection,
200 "Only POST method is allowed");
203 return 0;
207 /* Process first phase of TOTP request */
208 static void do_as_sendsms(const struct http_connection *connection,
209 const struct http_request *request,
210 const struct arguments_otp_authentication *arguments) {
211 if (arguments == NULL) {
212 http_send_response_500(connection,
213 "Third argument of do_as_sendsms() is NULL");
214 return;
217 if (request->method != HTTP_METHOD_POST) {
218 http_send_response_400(connection,
219 "First phase TOTP request must be POST");
220 return;
223 if (!http_client_authenticates(request)) {
224 http_send_response_401_otp(connection,
225 "totpsendsms",
226 "authentication.error.userIsNotAuthenticated",
227 "Client did not send any authentication header");
228 return;
231 switch(http_authenticate_basic(request,
232 arguments->username, arguments->password)) {
233 case HTTP_ERROR_SUCCESS: {
234 /* Find final URI */
235 char *uri = strstr(request->uri, "&uri=");
236 if (uri == NULL) {
237 http_send_response_400(connection,
238 "Missing uri parameter in Request URI");
239 return;
241 uri += 5;
242 /* Build new location for second OTP phase */
243 char *location = NULL;
244 if (-1 == test_asprintf(&location, "%s%s", as_path_dontsendsms, uri)) {
245 http_send_response_500(connection,
246 "Could not build new localtion for "
247 "second OTP phase");
248 return;
250 char *terminator = strchr(uri, '&');
251 if (NULL != terminator)
252 location[strlen(as_path_dontsendsms) + (uri - terminator)] = '\0';
253 http_send_response_302_totp(connection,
254 "authentication.info.totpSended",
255 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
256 location);
257 free(location);
259 break;
260 case HTTP_ERROR_CLIENT:
261 if (arguments->isds_deviations)
262 http_send_response_401_otp(connection,
263 "totpsendsms",
264 "authentication.error.userIsNotAuthenticated",
265 " Retry: Bad user name or password in first OTP phase.\r\n"
266 " This is very long header\r\n"
267 " which should span to more lines.\r\n"
268 " Surrounding LWS are meaning-less. ");
269 else
270 http_send_response_403(connection);
271 break;
272 default:
273 http_send_response_500(connection,
274 "Could not verify Basic authentication");
279 /* Return static string representation of HTTP OTP authentication method.
280 * Or NULL in case of error. */
281 static const char *auth_otp_method2string(enum auth_otp_method method) {
282 switch (method) {
283 case AUTH_OTP_HMAC: return "hotp";
284 case AUTH_OTP_TIME: return "totp";
285 default: return NULL;
290 /* Process second phase of OTP request */
291 static void do_as_phase_two(const struct http_connection *connection,
292 const struct http_request *request,
293 const struct arguments_otp_authentication *arguments) {
294 if (arguments == NULL) {
295 http_send_response_500(connection,
296 "Third argument of do_as_phase_two() is NULL");
297 return;
300 if (request->method != HTTP_METHOD_POST) {
301 http_send_response_400(connection,
302 "Second phase OTP request must be POST");
303 return;
306 if (!http_client_authenticates(request)) {
307 http_send_response_401_otp(connection,
308 auth_otp_method2string(arguments->method),
309 "authentication.error.userIsNotAuthenticated",
310 "Client did not send any authentication header");
311 return;
314 switch(http_authenticate_otp(request,
315 arguments->username, arguments->password, arguments->otp)) {
316 case HTTP_ERROR_SUCCESS: {
317 /* Find final URI */
318 char *uri = strstr(request->uri, "&uri=");
319 if (uri == NULL) {
320 http_send_response_400(connection,
321 "Missing uri parameter in Request URI");
322 return;
324 uri += 5;
325 /* Build new location for final request */
326 char *location = NULL;
327 if (NULL == (location = strdup(uri))) {
328 http_send_response_500(connection,
329 "Could not build new location for final request");
330 return;
332 char *terminator = strchr(location, '&');
333 if (NULL != terminator)
334 *terminator = '\0';
335 /* Generate pseudo-random cookie value. This is to prevent
336 * client from reusing the cookie accidentally. We use the
337 * same seed to get reproducible tests. */
338 if (-1 == test_asprintf(&authorization_cookie_value, "%d",
339 rand())) {
340 http_send_response_500(connection,
341 "Could not generate cookie value");
342 free(location);
343 return;
345 /* XXX: Add Path parameter to cookie, otherwise
346 * different paths will not match.
347 * FIXME: Domain argument does not work with cURL. Report a bug. */
348 http_send_response_302_cookie(connection,
349 authorization_cookie_name,
350 authorization_cookie_value,
351 /*http_find_host(request)*/NULL,
352 /*NULL*/"/",
353 location);
354 free(location);
356 break;
357 case HTTP_ERROR_CLIENT:
358 if (arguments->isds_deviations)
359 http_send_response_401_otp(connection,
360 auth_otp_method2string(arguments->method),
361 "authentication.error.userIsNotAuthenticated",
362 " Retry: Bad user name or password in second OTP phase.\r\n"
363 " This is very long header\r\n"
364 " which should span to more lines.\r\n"
365 " Surrounding LWS are meaning-less. ");
366 else
367 http_send_response_403(connection);
368 break;
369 default:
370 http_send_response_500(connection,
371 "Could not verify OTP authentication");
376 /* Process ASWS for changing OTP password requests */
377 /* FIXME: The ASWS URI hosts two services: for sending TOTP code for password
378 * change and for changing OTP password. The problem is the former one is
379 * basic-authenticated, the later one is otp-authenticated. But we cannot
380 * decide which authentication to enforce without understadning request body.
381 * I will just try both of them to choose the service.
382 * But I hope official server implementation does it in more clever way. */
383 static void do_asws(const struct http_connection *connection,
384 const struct http_request *request,
385 const struct arguments_otp_authentication *arguments) {
386 http_error error;
387 if (arguments == NULL) {
388 http_send_response_500(connection,
389 "Third argument of do_asws() is NULL");
390 return;
393 if (request->method != HTTP_METHOD_POST) {
394 http_send_response_400(connection, "ASWS request must be POST");
395 return;
398 if (!http_client_authenticates(request)) {
399 http_send_response_401_otp(connection,
400 auth_otp_method2string(arguments->method),
401 "authentication.error.userIsNotAuthenticated",
402 "Client did not send any authentication header");
403 return;
406 /* Try OTP */
407 error = http_authenticate_otp(request,
408 arguments->username, arguments->password, arguments->otp);
409 if (HTTP_ERROR_SUCCESS == error) {
410 /* This will be request for password change because OTP
411 * authentication succeeded. */
412 do_ws(connection, arguments->services, request, NULL);
413 return;
416 if (AUTH_OTP_TIME == arguments->method) {
417 /* Try Basic */
418 error = http_authenticate_basic(request,
419 arguments->username, arguments->password);
420 if (HTTP_ERROR_SUCCESS == error) {
421 /* This will be request for time code for password change because
422 * basic authentication succeeded. */
423 do_ws(connection, arguments->services, request, NULL);
424 return;
428 if (HTTP_ERROR_CLIENT == error) {
429 if (arguments->isds_deviations)
430 http_send_response_401_otp(connection,
431 auth_otp_method2string(arguments->method),
432 "authentication.error.userIsNotAuthenticated",
433 " Retry: Bad user name or password in Authorization.\r\n"
434 " This is very long header\r\n"
435 " which should span to more lines.\r\n"
436 " Surrounding LWS are meaning-less. ");
437 else
438 http_send_response_403(connection);
439 return;
442 http_send_response_500(connection,
443 "Could not verify OTP authentication");
447 /* Process OTP session cookie invalidation request */
448 static void do_as_logout(const struct http_connection *connection,
449 const struct http_request *request,
450 const struct arguments_otp_authentication *arguments) {
451 if (arguments == NULL) {
452 http_send_response_500(connection,
453 "Third argument of do_as_logout() is NULL");
454 return;
457 if (request->method != HTTP_METHOD_GET) {
458 http_send_response_400(connection,
459 "OTP cookie invalidation request must be GET");
460 return;
463 const char *received_cookie =
464 http_find_cookie(request, authorization_cookie_name);
466 if (authorization_cookie_value == NULL || received_cookie == NULL ||
467 strcmp(authorization_cookie_value, received_cookie)) {
468 http_send_response_403(connection);
469 return;
472 /* XXX: Add Path parameter to cookie, otherwise
473 * different paths will not match.
474 * FIXME: Domain argument does not work with cURL. Report a bug. */
475 http_send_response_200_cookie(connection,
476 authorization_cookie_name,
478 /*http_find_host(request)*/ NULL,
479 /*NULL*/"/",
480 NULL, 0, NULL);
484 /* Process ISDS WS ping authorized by cookie */
485 static void do_ws_with_cookie(const struct http_connection *connection,
486 const struct http_request *request,
487 const struct arguments_otp_authentication *arguments,
488 const char *valid_base_path) {
489 const char *received_cookie =
490 http_find_cookie(request, authorization_cookie_name);
492 if (authorization_cookie_value != NULL && received_cookie != NULL &&
493 !strcmp(authorization_cookie_value, received_cookie))
494 do_ws(connection, arguments->services, request, valid_base_path);
495 else
496 http_send_response_403(connection);
500 /* Do the server protocol with OTP authentication.
501 * @connection is HTTP connection
502 * @server_arguments is pointer to structure arguments_otp_authentication. It
503 * selects OTP method to enable.
504 * @request is parsed HTTP client requrest
505 * @return 0 to accept new client, return -1 in case of fatal error. */
506 int server_otp_authentication(const struct http_connection *connection,
507 const void *server_arguments, const struct http_request *request) {
508 const struct arguments_otp_authentication *arguments =
509 (const struct arguments_otp_authentication *) server_arguments;
511 if (NULL == arguments || NULL == request) {
512 return(-1);
515 if (arguments->username != NULL) {
516 if (arguments->method == AUTH_OTP_HMAC &&
517 !strncmp(request->uri, as_path_hotp, strlen(as_path_hotp))) {
518 do_as_phase_two(connection, request, arguments);
519 } else if (arguments->method == AUTH_OTP_TIME &&
520 !strncmp(request->uri, as_path_sendsms,
521 strlen(as_path_sendsms))) {
522 do_as_sendsms(connection, request, arguments);
523 } else if (arguments->method == AUTH_OTP_TIME &&
524 !strncmp(request->uri, as_path_dontsendsms,
525 strlen(as_path_dontsendsms))) {
526 do_as_phase_two(connection, request, arguments);
527 } else if (!strncmp(request->uri, as_path_logout,
528 strlen(as_path_logout))) {
529 do_as_logout(connection, request, arguments);
530 } else if (!strcmp(request->uri, asws_path)) {
531 do_asws(connection, request, arguments);
532 } else if (!strcmp(request->uri, ws_path)) {
533 do_ws_with_cookie(connection, request, arguments,
534 ws_base_path_otp);
535 } else {
536 http_send_response_400(connection,
537 "Unknown path for OTP authenticating service");
539 } else {
540 if (!strcmp(request->uri, ws_path)) {
541 do_ws(connection, arguments->services, request,
542 ws_base_path_otp);
543 } else {
544 http_send_response_400(connection,
545 "Unknown path for OTP authenticating service");
549 return 0;
553 /* Implementation of server that is out of order.
554 * It always sends back SOAP Fault with HTTP error 503.
555 * @connection is HTTP connection
556 * @server_arguments is ununsed pointer
557 * @request is parsed HTTP client request
558 * @return 0 to accept new client, return -1 in case of fatal error. */
559 int server_out_of_order(const struct http_connection *connection,
560 const void *server_arguments, const struct http_request *request) {
561 const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
562 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>";
564 http_send_response_503(connection, fault, strlen(fault),
565 soap_mime_type);
566 return 0;
570 /* Call-back for HTTP to receive data from socket
571 * API is equivalent to recv(2) except automatic interrupt handling. */
572 static ssize_t recv_plain(const struct http_connection *connection,
573 void *buffer, size_t length) {
574 ssize_t retval;
575 do {
576 retval = recv(connection->socket, buffer, length, 0);
577 } while (-1 == retval && EINTR == errno);
578 return retval;
582 /* Call-back for HTTP to sending data to socket
583 * API is equivalent to send(2) except automatic interrupt handling. */
584 static ssize_t send_plain(const struct http_connection *connection,
585 const void *buffer, size_t length) {
586 ssize_t retval;
587 do {
588 retval = send(connection->socket, buffer, length, MSG_NOSIGNAL);
589 } while (-1 == retval && EINTR == errno);
590 return retval;
594 /* Call-back for HTTP to receive data from TLS socket
595 * API is equivalent to recv(2) except automatic interrupt handling. */
596 static ssize_t recv_tls(const struct http_connection *connection,
597 void *buffer, size_t length) {
598 ssize_t retval;
599 do {
600 retval = gnutls_record_recv(connection->callback_data, buffer, length);
601 if (GNUTLS_E_REHANDSHAKE == retval) {
602 int error;
603 do {
604 error = gnutls_handshake(connection->callback_data);
605 } while (error < 0 && !gnutls_error_is_fatal(error));
606 if (error < 0) {
607 fprintf(stderr, "TLS rehandshake failed: %s\n",
608 gnutls_strerror(error));
609 return -1;
612 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
613 return (retval < 0) ? -1 : retval;
617 /* Call-back for HTTP to sending data to TLS socket
618 * API is equivalent to send(2) except automatic interrupt handling. */
619 static ssize_t send_tls(const struct http_connection *connection,
620 const void *buffer, size_t length) {
621 ssize_t retval;
622 do {
623 retval = gnutls_record_send(connection->callback_data, buffer, length);
624 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
625 return (retval < 0) ? -1 : retval;
629 /* Call-back fot GnuTLS to receive data from TCP socket.
630 * We override default implementation to unify passing http_connection as
631 * @context. Also otherwise there is need for ugly type cast from integer to
632 * pointer. */
633 static ssize_t tls_pull(gnutls_transport_ptr_t context, void* buffer,
634 size_t length) {
635 return recv( ((struct http_connection*)context)->socket,
636 buffer, length, 0);
640 /* Call-back fot GnuTLS to send data to TCP socket.
641 * GnuTLS does not call send(2) with MSG_NOSIGNAL, we must do it manually. */
642 static ssize_t tls_push(gnutls_transport_ptr_t context, const void* buffer,
643 size_t length) {
644 return send( ((struct http_connection*)context)->socket,
645 buffer, length, MSG_NOSIGNAL);
649 /* Verify client certificate from current TLS session.
650 * @tls_session is session in TLS handshake when client sent certificate
651 * @return 0 for acceptance, return non-0 for denial. */
652 static int tls_verify_client(gnutls_session_t tls_session) {
653 const gnutls_datum_t *chain; /* Pointer to static data */
654 unsigned int chain_length;
655 gnutls_x509_crt_t certificate;
656 gnutls_datum_t certificate_text;
657 unsigned int status;
658 int error;
660 /* Obtain client's certificate chain */
661 chain = gnutls_certificate_get_peers(tls_session, &chain_length);
662 if (NULL == chain) {
663 fprintf(stderr, "Error while obtaining client's certificate\n");
664 return -1;
666 if (chain_length < 1) {
667 fprintf(stderr, "Client did not send any certificate\n");
668 return -1;
671 /* Print client's certificate */
672 error = gnutls_x509_crt_init(&certificate);
673 if (error) {
674 fprintf(stderr, "Could not initialize certificate storage: %s\n",
675 gnutls_strerror(error));
676 return -1;
678 error = gnutls_x509_crt_import(certificate, chain,
679 GNUTLS_X509_FMT_DER);
680 if (error) {
681 fprintf(stderr, "Could not parse client's X.509 certificate: %s\n",
682 gnutls_strerror(error));
683 gnutls_x509_crt_deinit(certificate);
684 return -1;
686 error = gnutls_x509_crt_print(certificate, GNUTLS_CRT_PRINT_ONELINE,
687 &certificate_text);
688 if (error) {
689 fprintf(stderr, "Could not print client's certificate: %s\n",
690 gnutls_strerror(error));
691 gnutls_x509_crt_deinit(certificate);
692 return -1;
694 fprintf(stderr, "Client sent certificate: %s\n", certificate_text.data);
695 gnutls_free(certificate_text.data);
696 gnutls_x509_crt_deinit(certificate);
698 /* Verify certificate signature and path */
699 error = gnutls_certificate_verify_peers2(tls_session, &status);
700 if (error) {
701 fprintf(stderr, "Could not verify client's certificate: %s\n",
702 gnutls_strerror(error));
703 return -1;
705 if (status) {
706 fprintf(stderr, "Client's certificate is not valid.\n");
707 return -1;
708 } else {
709 fprintf(stderr, "Client's certificate is valid.\n");
711 return 0;
715 /* Start sever in separate process.
716 * @server_process is PID of forked server
717 * @server_address is automatically allocated TCP address of listening server
718 * @server_implementation points to kind of server to implement. Valid values
719 * are addresses of server_basic_authentication(),
720 * server_otp_authentication(), or server_out_of_order().
721 * @server_arguments is pointer to argument pass to @server_implementation. It
722 * usually contains:
723 * @username sets required user name server has to require. Set NULL to
724 * disable HTTP authentication.
725 * @password sets required password server has to require
726 * socket.
727 * @isds_deviations is flag to set conformance level. If false, server is
728 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
729 * specification. Otherwise server mimics real ISDS implementation as much
730 * as possible.
731 * @tls sets TLS layer. Pass NULL for plain HTTP.
732 * @return -1 in case of error. */
733 int start_server(pid_t *server_process, char **server_address,
734 int (*server_implementation)(const struct http_connection *,
735 const void *, const struct http_request *),
736 const void *server_arguments, const struct tls_authentication *tls) {
737 int server_socket;
738 int error;
740 if (server_address == NULL) {
741 set_server_error("start_server(): Got invalid server_address pointer");
742 return -1;
744 *server_address = NULL;
746 if (server_process == NULL) {
747 set_server_error("start_server(): Got invalid server_process pointer");
748 return -1;
751 server_socket = listen_on_socket();
752 if (server_socket == -1) {
753 set_server_error("Could not create listening socket");
754 return -1;
757 *server_address = socket2address(server_socket);
758 if (*server_address == NULL) {
759 close(server_socket);
760 set_server_error("Could not format address of listening address");
761 return -1;
764 if (NULL != tls && NULL == tls->server_certificate) {
765 /* XXX: X.509 TLS server requires server certificate. */
766 tls = NULL;
768 if (NULL != tls) {
769 const char *error_position;
770 if ((error = gnutls_global_init())) {
771 close(server_socket);
772 set_server_error("Could not initialize GnuTLS: %s",
773 gnutls_strerror(error));
774 return -1;
776 if ((error =
777 gnutls_certificate_allocate_credentials(&x509_credentials))) {
778 close(server_socket);
779 gnutls_global_deinit();
780 set_server_error("Could not allocate X.509 credentials: %s",
781 gnutls_strerror(error));
782 return -1;
784 if (NULL != tls->authority_certificate) {
785 if (0 > (error = gnutls_certificate_set_x509_trust_file(
786 x509_credentials, tls->authority_certificate,
787 GNUTLS_X509_FMT_PEM))) {
788 close(server_socket);
789 gnutls_certificate_free_credentials(x509_credentials);
790 gnutls_global_deinit();
791 set_server_error("Could not load authority certificate `%s': %s",
792 tls->authority_certificate, gnutls_strerror(error));
793 return -1;
796 if ((error = gnutls_certificate_set_x509_key_file(x509_credentials,
797 tls->server_certificate, tls->server_key,
798 GNUTLS_X509_FMT_PEM))) {
799 close(server_socket);
800 gnutls_certificate_free_credentials(x509_credentials);
801 gnutls_global_deinit();
802 set_server_error("Could not load server certificate or "
803 "private key `%s': %s", tls->server_certificate,
804 gnutls_strerror(error));
805 return -1;
807 if ((error = gnutls_priority_init(&priority_cache,
808 "PERFORMANCE", &error_position))) {
809 close(server_socket);
810 gnutls_certificate_free_credentials(x509_credentials);
811 gnutls_global_deinit();
812 if (error == GNUTLS_E_INVALID_REQUEST) {
813 set_server_error("Could not set TLS algorithm preferences: "
814 "%s Error at `%s'.",
815 gnutls_strerror(error), error_position);
816 set_server_error("Could not set TLS algorithm preferences: %s",
817 gnutls_strerror(error));
819 return -1;
821 /* XXX: priority_cache is linked from x509_credentials now.
822 * Deinitialization must free x509_credentials before priority_cache. */
824 if ((error = gnutls_dh_params_init(&dh_parameters))) {
825 close(server_socket);
826 gnutls_certificate_free_credentials(x509_credentials);
827 gnutls_priority_deinit(priority_cache);
828 gnutls_global_deinit();
829 set_server_error("Could not allocate Diffie-Hellman parameters: "
830 "%s", gnutls_strerror(error));
831 return -1;
833 if ((error = gnutls_dh_params_generate2(dh_parameters,
834 gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
835 GNUTLS_SEC_PARAM_LOW)))) {
836 close(server_socket);
837 gnutls_certificate_free_credentials(x509_credentials);
838 gnutls_priority_deinit(priority_cache);
839 gnutls_dh_params_deinit(dh_parameters);
840 gnutls_global_deinit();
841 set_server_error("Could not generate Diffie-Hellman parameters: %s",
842 gnutls_strerror(error));
843 return -1;
845 gnutls_certificate_set_dh_params(x509_credentials, dh_parameters);
846 /* XXX: dh_parameters are linked from x509_credentials now.
847 * Deinitialization must free x509_credentials before dh_parameters. */
850 *server_process = fork();
851 if (*server_process == -1) {
852 close(server_socket);
853 if (NULL != tls) {
854 gnutls_certificate_free_credentials(x509_credentials);
855 gnutls_priority_deinit(priority_cache);
856 gnutls_dh_params_deinit(dh_parameters);
857 gnutls_global_deinit();
859 set_server_error("Server could not been forked");
860 return -1;
863 if (*server_process == 0) {
864 int client_socket;
865 gnutls_session_t tls_session;
866 struct http_connection connection;
867 struct http_request *request = NULL;
868 http_error error;
869 int terminate = 0;
871 while (!terminate) {
872 if (NULL != tls) {
873 if ((error = gnutls_init(&tls_session, GNUTLS_SERVER))) {
874 set_server_error("Could not initialize TLS session: %s",
875 gnutls_strerror(error));
876 terminate = -1;
877 continue;
879 if ((error = gnutls_priority_set(tls_session,
880 priority_cache))) {
881 set_server_error(
882 "Could not set priorities to TLS session: %s",
883 gnutls_strerror(error));
884 terminate = -1;
885 gnutls_deinit(tls_session);
886 continue;
888 if ((error = gnutls_credentials_set(tls_session,
889 GNUTLS_CRD_CERTIFICATE, x509_credentials))) {
890 set_server_error("Could not set X509 credentials to TLS "
891 "session: %s", gnutls_strerror(error));
892 terminate = -1;
893 gnutls_deinit(tls_session);
894 continue;
896 /* XXX: Credentials are linked from session now.
897 * Deinitializition must free session before x509_credentials.
899 if (NULL != tls->client_name) {
900 /* Require client certificate */
901 gnutls_certificate_server_set_request(tls_session,
902 GNUTLS_CERT_REQUIRE);
903 /* And verify it in TLS handshake */
904 gnutls_certificate_set_verify_function(x509_credentials,
905 tls_verify_client);
909 if (0 > (client_socket = accept(server_socket, NULL, NULL))) {
910 terminate = -1;
911 if (NULL != tls)
912 gnutls_deinit(tls_session);
913 continue;
915 fprintf(stderr, "Connection accepted\n");
916 connection.socket = client_socket;
918 if (NULL == tls) {
919 connection.recv_callback = recv_plain;
920 connection.send_callback = send_plain;
921 connection.callback_data = NULL;
922 } else {
923 connection.recv_callback = recv_tls;
924 connection.send_callback = send_tls;
925 connection.callback_data = tls_session;
926 gnutls_transport_set_pull_function(tls_session, tls_pull);
927 gnutls_transport_set_push_function(tls_session, tls_push);
928 gnutls_transport_set_ptr2(tls_session,
929 &connection, &connection);
930 do {
931 error = gnutls_handshake(tls_session);
932 } while (error < 0 && !gnutls_error_is_fatal(error));
933 if (error < 0) {
934 fprintf(stderr, "TLS handshake failed: %s\n",
935 gnutls_strerror(error));
936 close(client_socket);
937 gnutls_deinit(tls_session);
938 continue;
942 error = http_read_request(&connection, &request);
943 if (error) {
944 fprintf(stderr, "Error while reading request\n");
945 if (error == HTTP_ERROR_CLIENT)
946 http_send_response_400(&connection, "Error in request");
947 else
948 http_send_response_500(&connection,
949 "Could not read request");
950 close(client_socket);
951 if (NULL != tls)
952 gnutls_deinit(tls_session);
953 continue;
956 terminate = server_implementation(&connection, server_arguments,
957 request);
959 http_request_free(&request);
960 close(client_socket);
961 if (NULL != tls) {
962 gnutls_deinit(tls_session);
966 close(server_socket);
967 free(authorization_cookie_value);
968 exit(EXIT_SUCCESS);
969 /* Does not return */
972 return 0;
976 /* Kill the server process.
977 * Return 0. Return -1 if server could not been stopped. Return 1 if server
978 * crashed. */
979 int stop_server(pid_t server_process) {
980 int status;
981 if (server_process <= 0) {
982 set_server_error("Invalid server PID to kill");
983 return -1;
985 if (-1 == kill(server_process, SIGTERM)) {
986 set_server_error("Could not terminate server");
987 return -1;
989 if (-1 == waitpid(server_process, &status, 0)) {
990 set_server_error("Could not wait for server termination");
991 return -1;
993 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
994 set_server_error("Server terminated by signal %d violently",
995 WTERMSIG(status));
996 return 1;
998 return 0;