test: Deallocate all memory in isds_Address_duplicate tests
[libisds.git] / test / simline / server.c
blobed707dc5f0eb54a68c71e24ce44f0d184715f0ed
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_commercial_certificate_with_password = "/certds/";
38 static const char *ws_base_path_otp = "/apps/";
40 static const char *authorization_cookie_name = "IPCZ-X-COOKIE";
41 static char *authorization_cookie_value = NULL;
43 /* TLS stuff */
44 static gnutls_certificate_credentials_t x509_credentials;
45 static gnutls_priority_t priority_cache;
46 static gnutls_dh_params_t dh_parameters;
47 static gnutls_datum_t ticket_key;
48 static const char *client_required_dn = NULL;
50 /* Save error message if not yet set. The message will be duplicated.
51 * @message is printf(3) formatting string. */
52 void set_server_error(const char *message, ...) {
53 if (server_error == NULL) {
54 va_list ap;
55 va_start(ap, message);
56 test_vasprintf(&server_error, message, ap);
57 va_end(ap);
62 /* Creates listening TCP socket on localhost.
63 * Returns the socket descriptor or -1. */
64 int listen_on_socket(void) {
65 int retval;
66 struct addrinfo hints;
67 struct addrinfo *addresses, *address;
68 int fd;
70 memset(&hints, 0, sizeof(hints));
71 hints.ai_family = AF_UNSPEC;
72 hints.ai_socktype = SOCK_STREAM;
73 retval = getaddrinfo("localhost", NULL, &hints, &addresses);
74 if (retval) {
75 set_server_error("Could not resolve `localhost'");
76 return -1;
79 for (address = addresses; address != NULL; address = address->ai_next) {
80 fd = socket(address->ai_family, address->ai_socktype,
81 address->ai_protocol);
82 if (fd == -1) continue;
84 retval = bind(fd, address->ai_addr, address->ai_addrlen);
85 if (retval != 0) {
86 close(fd);
87 continue;
90 retval = listen(fd, 0);
91 if (retval == 0) {
92 freeaddrinfo(addresses);
93 return fd;
96 close(fd);
99 freeaddrinfo(addresses);
100 set_server_error("Could not start listening on TCP/localhost");
101 return -1;
105 /* Format socket address as printable string.
106 * @return allocated string or NULL in case of error. */
107 char *socket2address(int socket) {
108 struct sockaddr_storage storage;
109 socklen_t length = (socklen_t) sizeof(storage);
110 char host[NI_MAXHOST];
111 char service[NI_MAXSERV];
112 char *address = NULL;
114 if (-1 == getsockname(socket, (struct sockaddr *)&storage, &length)) {
115 set_server_error("Could not get address of server socket");
116 return NULL;
119 if (0 != getnameinfo((struct sockaddr *)&storage, length,
120 host, sizeof(host), service, sizeof(service),
121 NI_NUMERICHOST|NI_NUMERICSERV)) {
122 set_server_error("Could not resolve address of server socket");
123 return NULL;
126 if (-1 == test_asprintf(&address,
127 (strchr(host, ':') == NULL) ? "%s:%s" : "[%s]:%s",
128 host, service)) {
129 set_server_error("Could not format server address");
130 return NULL;
133 return address;
137 /* Format HTTP(s) socket address as printable URL string.
138 * @socket is listening TCP socket of HTTP server
139 * @tls is true for HTTPS, false for plain HTTP
140 * @return allocated string or NULL in case of error. */
141 static char *socket2url(int socket, _Bool tls) {
142 char *socket_address = NULL;
143 char *address = NULL;
145 socket_address = socket2address(socket);
146 if (NULL == socket_address) {
147 set_server_error("Could not format server address");
148 free(socket_address);
149 return NULL;
152 if (-1 == test_asprintf(&address, "%s://%s/",
153 (tls) ? "https" : "http", socket_address)) {
154 set_server_error("Could not format server address");
155 free(socket_address);
156 return NULL;
159 free(socket_address);
160 return address;
164 /* Process ISDS web service */
165 static void do_ws(const struct http_connection *connection,
166 const struct service_configuration *ws_configuration,
167 const struct http_request *request, const char *required_base_path) {
168 char *end_point = request->uri; /* Pointer to string in the request */
170 if (request->method != HTTP_METHOD_POST) {
171 http_send_response_400(connection,
172 "Regular ISDS web service request must be POST");
173 return;
176 if (required_base_path != NULL) {
177 size_t required_base_path_length = strlen(required_base_path);
178 if (strncmp(end_point, required_base_path, required_base_path_length)) {
179 http_send_response_400(connection, "Request sent to invalid path");
180 return;
182 end_point += required_base_path_length;
185 soap(connection, ws_configuration, request->body, request->body_length,
186 end_point);
190 /* Do the server protocol.
191 * @connection is HTTP connection
192 * @server_arguments is pointer to structure arguments_basic_authentication
193 * @request is parsed HTTP client request
194 * @prefix is HTTP URI path prefix (directory) where all ISDS services live
195 * @return 0 to accept new client, return -1 in case of fatal error. */
196 static int server_prefixed_basic_authentication(
197 const struct http_connection *connection, const void *server_arguments,
198 const struct http_request *request, const char *prefix) {
199 const struct arguments_basic_authentication *arguments =
200 (const struct arguments_basic_authentication *) server_arguments;
202 if (NULL == arguments || NULL == request || NULL == prefix) {
203 return -1;
206 if (request->method == HTTP_METHOD_POST) {
207 /* Only POST requests are used in Basic authentication mode */
208 if (arguments->username != NULL) {
209 if (http_client_authenticates(request)) {
210 switch(http_authenticate_basic(request,
211 arguments->username, arguments->password)) {
212 case HTTP_ERROR_SUCCESS:
213 do_ws(connection, arguments->services, request,
214 prefix);
215 break;
216 case HTTP_ERROR_CLIENT:
217 if (arguments->isds_deviations)
218 http_send_response_401_basic(connection);
219 else
220 http_send_response_403(connection);
221 break;
222 default:
223 http_send_response_500(connection,
224 "Server error while verifying Basic "
225 "authentication");
227 } else {
228 http_send_response_401_basic(connection);
230 } else {
231 do_ws(connection, arguments->services, request,
232 prefix);
234 } else {
235 /* HTTP method unsupported per ISDS specification */
236 http_send_response_400(connection,
237 "Only POST method is allowed");
240 return 0;
244 /* Do the server protocol.
245 * @connection is HTTP connection
246 * @server_arguments is pointer to structure arguments_basic_authentication
247 * @request is parsed HTTP client request
248 * @return 0 to accept new client, return -1 in case of fatal error. */
249 int server_basic_authentication(const struct http_connection *connection,
250 const void *server_arguments, const struct http_request *request) {
251 return server_prefixed_basic_authentication(connection, server_arguments,
252 request, ws_base_path_basic);
256 /* Do the server protocol.
257 * @connection is HTTP connection
258 * @server_arguments is pointer to structure arguments_basic_authentication
259 * @request is parsed HTTP client request
260 * @return 0 to accept new client, return -1 in case of fatal error. */
261 int server_certificate_with_password_authentication(
262 const struct http_connection *connection,
263 const void *server_arguments, const struct http_request *request) {
264 return server_prefixed_basic_authentication(connection, server_arguments,
265 request, ws_base_path_commercial_certificate_with_password);
269 /* Process first phase of TOTP request */
270 static void do_as_sendsms(const struct http_connection *connection,
271 const struct http_request *request,
272 const struct arguments_otp_authentication *arguments) {
273 if (arguments == NULL) {
274 http_send_response_500(connection,
275 "Third argument of do_as_sendsms() is NULL");
276 return;
279 if (request->method != HTTP_METHOD_POST) {
280 http_send_response_400(connection,
281 "First phase TOTP request must be POST");
282 return;
285 if (!http_client_authenticates(request)) {
286 http_send_response_401_otp(connection,
287 "totpsendsms",
288 "authentication.error.userIsNotAuthenticated",
289 "Client did not send any authentication header");
290 return;
293 switch(http_authenticate_basic(request,
294 arguments->username, arguments->password)) {
295 case HTTP_ERROR_SUCCESS: {
296 /* Find final URI */
297 char *uri = strstr(request->uri, "&uri=");
298 if (uri == NULL) {
299 http_send_response_400(connection,
300 "Missing uri parameter in Request URI");
301 return;
303 uri += 5;
304 /* Build new location for second OTP phase */
305 char *location = NULL;
306 if (-1 == test_asprintf(&location, "%s%s", as_path_dontsendsms, uri)) {
307 http_send_response_500(connection,
308 "Could not build new localtion for "
309 "second OTP phase");
310 return;
312 char *terminator = strchr(uri, '&');
313 if (NULL != terminator)
314 location[strlen(as_path_dontsendsms) + (uri - terminator)] = '\0';
315 http_send_response_302_totp(connection,
316 "authentication.info.totpSended",
317 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
318 location);
319 free(location);
321 break;
322 case HTTP_ERROR_CLIENT:
323 if (arguments->isds_deviations)
324 http_send_response_401_otp(connection,
325 "totpsendsms",
326 "authentication.error.userIsNotAuthenticated",
327 " Retry: Bad user name or password in first OTP phase.\r\n"
328 " This is very long header\r\n"
329 " which should span to more lines.\r\n"
330 " Surrounding LWS are meaning-less. ");
331 else
332 http_send_response_403(connection);
333 break;
334 default:
335 http_send_response_500(connection,
336 "Could not verify Basic authentication");
341 /* Return static string representation of HTTP OTP authentication method.
342 * Or NULL in case of error. */
343 static const char *auth_otp_method2string(enum auth_otp_method method) {
344 switch (method) {
345 case AUTH_OTP_HMAC: return "hotp";
346 case AUTH_OTP_TIME: return "totp";
347 default: return NULL;
352 /* Process second phase of OTP request */
353 static void do_as_phase_two(const struct http_connection *connection,
354 const struct http_request *request,
355 const struct arguments_otp_authentication *arguments) {
356 if (arguments == NULL) {
357 http_send_response_500(connection,
358 "Third argument of do_as_phase_two() is NULL");
359 return;
362 if (request->method != HTTP_METHOD_POST) {
363 http_send_response_400(connection,
364 "Second phase OTP request must be POST");
365 return;
368 if (!http_client_authenticates(request)) {
369 http_send_response_401_otp(connection,
370 auth_otp_method2string(arguments->method),
371 "authentication.error.userIsNotAuthenticated",
372 "Client did not send any authentication header");
373 return;
376 switch(http_authenticate_otp(request,
377 arguments->username, arguments->password, arguments->otp)) {
378 case HTTP_ERROR_SUCCESS: {
379 /* Find final URI */
380 char *uri = strstr(request->uri, "&uri=");
381 if (uri == NULL) {
382 http_send_response_400(connection,
383 "Missing uri parameter in Request URI");
384 return;
386 uri += 5;
387 /* Build new location for final request */
388 char *location = NULL;
389 if (NULL == (location = strdup(uri))) {
390 http_send_response_500(connection,
391 "Could not build new location for final request");
392 return;
394 char *terminator = strchr(location, '&');
395 if (NULL != terminator)
396 *terminator = '\0';
397 /* Generate pseudo-random cookie value. This is to prevent
398 * client from reusing the cookie accidentally. We use the
399 * same seed to get reproducible tests. */
400 if (-1 == test_asprintf(&authorization_cookie_value, "%d",
401 rand())) {
402 http_send_response_500(connection,
403 "Could not generate cookie value");
404 free(location);
405 return;
407 /* XXX: Add Path parameter to cookie, otherwise
408 * different paths will not match.
409 * FIXME: Domain argument does not work with cURL. Report a bug. */
410 http_send_response_302_cookie(connection,
411 authorization_cookie_name,
412 authorization_cookie_value,
413 /*http_find_host(request)*/NULL,
414 /*NULL*/"/",
415 location);
416 free(location);
418 break;
419 case HTTP_ERROR_CLIENT:
420 if (arguments->isds_deviations)
421 http_send_response_401_otp(connection,
422 auth_otp_method2string(arguments->method),
423 "authentication.error.userIsNotAuthenticated",
424 " Retry: Bad user name or password in second OTP phase.\r\n"
425 " This is very long header\r\n"
426 " which should span to more lines.\r\n"
427 " Surrounding LWS are meaning-less. ");
428 else
429 http_send_response_403(connection);
430 break;
431 default:
432 http_send_response_500(connection,
433 "Could not verify OTP authentication");
438 /* Process ASWS for changing OTP password requests */
439 /* FIXME: The ASWS URI hosts two services: for sending TOTP code for password
440 * change and for changing OTP password. The problem is the former one is
441 * basic-authenticated, the later one is otp-authenticated. But we cannot
442 * decide which authentication to enforce without understadning request body.
443 * I will just try both of them to choose the service.
444 * But I hope official server implementation does it in more clever way. */
445 static void do_asws(const struct http_connection *connection,
446 const struct http_request *request,
447 const struct arguments_otp_authentication *arguments) {
448 http_error error;
449 if (arguments == NULL) {
450 http_send_response_500(connection,
451 "Third argument of do_asws() is NULL");
452 return;
455 if (request->method != HTTP_METHOD_POST) {
456 http_send_response_400(connection, "ASWS request must be POST");
457 return;
460 if (!http_client_authenticates(request)) {
461 http_send_response_401_otp(connection,
462 auth_otp_method2string(arguments->method),
463 "authentication.error.userIsNotAuthenticated",
464 "Client did not send any authentication header");
465 return;
468 /* Try OTP */
469 error = http_authenticate_otp(request,
470 arguments->username, arguments->password, arguments->otp);
471 if (HTTP_ERROR_SUCCESS == error) {
472 /* This will be request for password change because OTP
473 * authentication succeeded. */
474 do_ws(connection, arguments->services, request, NULL);
475 return;
478 if (AUTH_OTP_TIME == arguments->method) {
479 /* Try Basic */
480 error = http_authenticate_basic(request,
481 arguments->username, arguments->password);
482 if (HTTP_ERROR_SUCCESS == error) {
483 /* This will be request for time code for password change because
484 * basic authentication succeeded. */
485 do_ws(connection, arguments->services, request, NULL);
486 return;
490 if (HTTP_ERROR_CLIENT == error) {
491 if (arguments->isds_deviations)
492 http_send_response_401_otp(connection,
493 auth_otp_method2string(arguments->method),
494 "authentication.error.userIsNotAuthenticated",
495 " Retry: Bad user name or password in Authorization.\r\n"
496 " This is very long header\r\n"
497 " which should span to more lines.\r\n"
498 " Surrounding LWS are meaning-less. ");
499 else
500 http_send_response_403(connection);
501 return;
504 http_send_response_500(connection,
505 "Could not verify OTP authentication");
509 /* Process OTP session cookie invalidation request */
510 static void do_as_logout(const struct http_connection *connection,
511 const struct http_request *request,
512 const struct arguments_otp_authentication *arguments) {
513 if (arguments == NULL) {
514 http_send_response_500(connection,
515 "Third argument of do_as_logout() is NULL");
516 return;
519 if (request->method != HTTP_METHOD_GET) {
520 http_send_response_400(connection,
521 "OTP cookie invalidation request must be GET");
522 return;
525 const char *received_cookie =
526 http_find_cookie(request, authorization_cookie_name);
528 if (authorization_cookie_value == NULL || received_cookie == NULL ||
529 strcmp(authorization_cookie_value, received_cookie)) {
530 http_send_response_403(connection);
531 return;
534 /* XXX: Add Path parameter to cookie, otherwise
535 * different paths will not match.
536 * FIXME: Domain argument does not work with cURL. Report a bug. */
537 http_send_response_200_cookie(connection,
538 authorization_cookie_name,
540 /*http_find_host(request)*/ NULL,
541 /*NULL*/"/",
542 NULL, 0, NULL);
546 /* Process ISDS WS ping authorized by cookie */
547 static void do_ws_with_cookie(const struct http_connection *connection,
548 const struct http_request *request,
549 const struct arguments_otp_authentication *arguments,
550 const char *valid_base_path) {
551 const char *received_cookie =
552 http_find_cookie(request, authorization_cookie_name);
554 if (authorization_cookie_value != NULL && received_cookie != NULL &&
555 !strcmp(authorization_cookie_value, received_cookie))
556 do_ws(connection, arguments->services, request, valid_base_path);
557 else
558 http_send_response_403(connection);
562 /* Do the server protocol with OTP authentication.
563 * @connection is HTTP connection
564 * @server_arguments is pointer to structure arguments_otp_authentication. It
565 * selects OTP method to enable.
566 * @request is parsed HTTP client requrest
567 * @return 0 to accept new client, return -1 in case of fatal error. */
568 int server_otp_authentication(const struct http_connection *connection,
569 const void *server_arguments, const struct http_request *request) {
570 const struct arguments_otp_authentication *arguments =
571 (const struct arguments_otp_authentication *) server_arguments;
573 if (NULL == arguments || NULL == request) {
574 return(-1);
577 if (arguments->username != NULL) {
578 if (arguments->method == AUTH_OTP_HMAC &&
579 !strncmp(request->uri, as_path_hotp, strlen(as_path_hotp))) {
580 do_as_phase_two(connection, request, arguments);
581 } else if (arguments->method == AUTH_OTP_TIME &&
582 !strncmp(request->uri, as_path_sendsms,
583 strlen(as_path_sendsms))) {
584 do_as_sendsms(connection, request, arguments);
585 } else if (arguments->method == AUTH_OTP_TIME &&
586 !strncmp(request->uri, as_path_dontsendsms,
587 strlen(as_path_dontsendsms))) {
588 do_as_phase_two(connection, request, arguments);
589 } else if (!strncmp(request->uri, as_path_logout,
590 strlen(as_path_logout))) {
591 do_as_logout(connection, request, arguments);
592 } else if (!strcmp(request->uri, asws_path)) {
593 do_asws(connection, request, arguments);
594 } else if (!strcmp(request->uri, ws_path)) {
595 do_ws_with_cookie(connection, request, arguments,
596 ws_base_path_otp);
597 } else {
598 http_send_response_400(connection,
599 "Unknown path for OTP authenticating service");
601 } else {
602 if (!strcmp(request->uri, ws_path)) {
603 do_ws(connection, arguments->services, request,
604 ws_base_path_otp);
605 } else {
606 http_send_response_400(connection,
607 "Unknown path for OTP authenticating service");
611 return 0;
615 /* Implementation of server that is out of order.
616 * It always sends back SOAP Fault with HTTP error 503.
617 * @connection is HTTP connection
618 * @server_arguments is ununsed pointer
619 * @request is parsed HTTP client request
620 * @return 0 to accept new client, return -1 in case of fatal error. */
621 int server_out_of_order(const struct http_connection *connection,
622 const void *server_arguments, const struct http_request *request) {
623 const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
624 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>";
626 http_send_response_503(connection, fault, strlen(fault),
627 soap_mime_type);
628 return 0;
632 /* Call-back for HTTP to receive data from socket
633 * API is equivalent to recv(2) except automatic interrupt handling. */
634 static ssize_t recv_plain(const struct http_connection *connection,
635 void *buffer, size_t length) {
636 ssize_t retval;
637 do {
638 retval = recv(connection->socket, buffer, length, 0);
639 } while (-1 == retval && EINTR == errno);
640 return retval;
644 /* Call-back for HTTP to sending data to socket
645 * API is equivalent to send(2) except automatic interrupt handling. */
646 static ssize_t send_plain(const struct http_connection *connection,
647 const void *buffer, size_t length) {
648 ssize_t retval;
649 do {
650 retval = send(connection->socket, buffer, length, MSG_NOSIGNAL);
651 } while (-1 == retval && EINTR == errno);
652 return retval;
656 /* Call-back for HTTP to receive data from TLS socket
657 * API is equivalent to recv(2) except automatic interrupt handling. */
658 static ssize_t recv_tls(const struct http_connection *connection,
659 void *buffer, size_t length) {
660 ssize_t retval;
661 do {
662 retval = gnutls_record_recv(connection->callback_data, buffer, length);
663 if (GNUTLS_E_REHANDSHAKE == retval) {
664 int error;
665 do {
666 error = gnutls_handshake(connection->callback_data);
667 } while (error < 0 && !gnutls_error_is_fatal(error));
668 if (error < 0) {
669 fprintf(stderr, "TLS rehandshake failed: %s\n",
670 gnutls_strerror(error));
671 return -1;
674 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
675 return (retval < 0) ? -1 : retval;
679 /* Call-back for HTTP to sending data to TLS socket
680 * API is equivalent to send(2) except automatic interrupt handling. */
681 static ssize_t send_tls(const struct http_connection *connection,
682 const void *buffer, size_t length) {
683 ssize_t retval;
684 do {
685 retval = gnutls_record_send(connection->callback_data, buffer, length);
686 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
687 return (retval < 0) ? -1 : retval;
691 /* Call-back fot GnuTLS to receive data from TCP socket.
692 * We override default implementation to unify passing http_connection as
693 * @context. Also otherwise there is need for ugly type cast from integer to
694 * pointer. */
695 static ssize_t tls_pull(gnutls_transport_ptr_t context, void* buffer,
696 size_t length) {
697 return recv( ((struct http_connection*)context)->socket,
698 buffer, length, 0);
702 /* Call-back fot GnuTLS to send data to TCP socket.
703 * GnuTLS does not call send(2) with MSG_NOSIGNAL, we must do it manually. */
704 static ssize_t tls_push(gnutls_transport_ptr_t context, const void* buffer,
705 size_t length) {
706 return send( ((struct http_connection*)context)->socket,
707 buffer, length, MSG_NOSIGNAL);
711 /* Verify client certificate from current TLS session.
712 * @tls_session is session in TLS handshake when client sent certificate
713 * @return 0 for acceptance, return non-0 for denial. */
714 static int tls_verify_client(gnutls_session_t tls_session) {
715 const gnutls_datum_t *chain; /* Pointer to static data */
716 unsigned int chain_length;
717 gnutls_x509_crt_t certificate;
718 gnutls_datum_t certificate_text;
719 unsigned int status;
720 char *dn_text;
721 size_t dn_size;
722 int error;
724 /* Obtain client's certificate chain */
725 chain = gnutls_certificate_get_peers(tls_session, &chain_length);
726 if (NULL == chain) {
727 fprintf(stderr, "Error while obtaining client's certificate\n");
728 return -1;
730 if (chain_length < 1) {
731 fprintf(stderr, "Client did not send any certificate\n");
732 return -1;
735 /* Print client's certificate */
736 error = gnutls_x509_crt_init(&certificate);
737 if (error) {
738 fprintf(stderr, "Could not initialize certificate storage: %s\n",
739 gnutls_strerror(error));
740 return -1;
742 error = gnutls_x509_crt_import(certificate, chain,
743 GNUTLS_X509_FMT_DER);
744 if (error) {
745 fprintf(stderr, "Could not parse client's X.509 certificate: %s\n",
746 gnutls_strerror(error));
747 gnutls_x509_crt_deinit(certificate);
748 return -1;
750 error = gnutls_x509_crt_print(certificate, GNUTLS_CRT_PRINT_ONELINE,
751 &certificate_text);
752 if (error) {
753 fprintf(stderr, "Could not print client's certificate: %s\n",
754 gnutls_strerror(error));
755 gnutls_x509_crt_deinit(certificate);
756 return -1;
758 fprintf(stderr, "Client sent certificate: %s\n", certificate_text.data);
759 gnutls_free(certificate_text.data);
761 /* Verify certificate signature and path */
762 error = gnutls_certificate_verify_peers2(tls_session, &status);
763 if (error) {
764 fprintf(stderr, "Could not verify client's certificate: %s\n",
765 gnutls_strerror(error));
766 gnutls_x509_crt_deinit(certificate);
767 return -1;
769 if (status) {
770 fprintf(stderr, "Client's certificate is not valid.\n");
771 gnutls_x509_crt_deinit(certificate);
772 return -1;
773 } else {
774 fprintf(stderr, "Client's certificate is valid.\n");
777 /* Authorize client */
778 error = gnutls_x509_crt_get_dn(certificate, NULL, &dn_size);
779 if (error != GNUTLS_E_SHORT_MEMORY_BUFFER) {
780 fprintf(stderr, "Could not determine client's "
781 "distinguished name size: %s.\n",
782 gnutls_strerror(error));
783 gnutls_x509_crt_deinit(certificate);
784 return -1;
786 dn_text = gnutls_malloc(dn_size);
787 if (NULL == dn_text) {
788 fprintf(stderr, "Could not allocate memory for client's "
789 "distinguished name.\n");
790 gnutls_x509_crt_deinit(certificate);
791 return -1;
793 error = gnutls_x509_crt_get_dn(certificate, dn_text, &dn_size);
794 if (error) {
795 fprintf(stderr, "Could obtain client's "
796 "distinguished name size: %s.\n",
797 gnutls_strerror(error));
798 gnutls_free(dn_text);
799 gnutls_x509_crt_deinit(certificate);
800 return -1;
802 gnutls_x509_crt_deinit(certificate);
803 if (client_required_dn != NULL &&
804 strcmp(client_required_dn, dn_text)) {
805 fprintf(stderr, "Client is not authorized: "
806 "Client's distinguished name `%s' does not match "
807 "required name `%s'.\n",
808 dn_text, client_required_dn);
809 gnutls_free(dn_text);
810 return -1;
812 fprintf(stderr, "Client is authorized.\n");
813 gnutls_free(dn_text);
814 return 0;
818 /* Start sever in separate process.
819 * @server_process is PID of forked server
820 * @server_address is automatically allocated TCP address of listening server
821 * @server_implementation points to kind of server to implement. Valid values
822 * are addresses of server_basic_authentication(),
823 * server_otp_authentication(), or server_out_of_order().
824 * @server_arguments is pointer to argument pass to @server_implementation. It
825 * usually contains:
826 * @username sets required user name server has to require. Set NULL to
827 * disable HTTP authentication.
828 * @password sets required password server has to require
829 * socket.
830 * @isds_deviations is flag to set conformance level. If false, server is
831 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
832 * specification. Otherwise server mimics real ISDS implementation as much
833 * as possible.
834 * @tls sets TLS layer. Pass NULL for plain HTTP.
835 * @return -1 in case of error. */
836 int start_server(pid_t *server_process, char **server_address,
837 int (*server_implementation)(const struct http_connection *,
838 const void *, const struct http_request *),
839 const void *server_arguments, const struct tls_authentication *tls) {
840 int server_socket;
841 int error;
843 if (server_address == NULL) {
844 set_server_error("start_server(): Got invalid server_address pointer");
845 return -1;
847 *server_address = NULL;
849 if (server_process == NULL) {
850 set_server_error("start_server(): Got invalid server_process pointer");
851 return -1;
854 if (NULL != tls && NULL == tls->server_certificate) {
855 /* XXX: X.509 TLS server requires server certificate. */
856 tls = NULL;
859 server_socket = listen_on_socket();
860 if (server_socket == -1) {
861 set_server_error("Could not create listening socket");
862 return -1;
865 *server_address = socket2url(server_socket, NULL != tls);
866 if (*server_address == NULL) {
867 close(server_socket);
868 set_server_error("Could not format address of listening socket");
869 return -1;
872 if (NULL != tls) {
873 const char *error_position;
874 if ((error = gnutls_global_init())) {
875 close(server_socket);
876 set_server_error("Could not initialize GnuTLS: %s",
877 gnutls_strerror(error));
878 return -1;
880 if ((error =
881 gnutls_certificate_allocate_credentials(&x509_credentials))) {
882 close(server_socket);
883 gnutls_global_deinit();
884 set_server_error("Could not allocate X.509 credentials: %s",
885 gnutls_strerror(error));
886 return -1;
888 if (NULL != tls->authority_certificate) {
889 if (0 > (error = gnutls_certificate_set_x509_trust_file(
890 x509_credentials, tls->authority_certificate,
891 GNUTLS_X509_FMT_PEM))) {
892 close(server_socket);
893 gnutls_certificate_free_credentials(x509_credentials);
894 gnutls_global_deinit();
895 set_server_error("Could not load authority certificate `%s': %s",
896 tls->authority_certificate, gnutls_strerror(error));
897 return -1;
900 if ((error = gnutls_certificate_set_x509_key_file(x509_credentials,
901 tls->server_certificate, tls->server_key,
902 GNUTLS_X509_FMT_PEM))) {
903 close(server_socket);
904 gnutls_certificate_free_credentials(x509_credentials);
905 gnutls_global_deinit();
906 set_server_error("Could not load server certificate or "
907 "private key `%s': %s", tls->server_certificate,
908 gnutls_strerror(error));
909 return -1;
911 if ((error = gnutls_priority_init(&priority_cache,
912 "PERFORMANCE", &error_position))) {
913 close(server_socket);
914 gnutls_certificate_free_credentials(x509_credentials);
915 gnutls_global_deinit();
916 if (error == GNUTLS_E_INVALID_REQUEST) {
917 set_server_error("Could not set TLS algorithm preferences: "
918 "%s Error at `%s'.",
919 gnutls_strerror(error), error_position);
920 set_server_error("Could not set TLS algorithm preferences: %s",
921 gnutls_strerror(error));
923 return -1;
925 /* XXX: priority_cache is linked from x509_credentials now.
926 * Deinitialization must free x509_credentials before priority_cache. */
928 if ((error = gnutls_dh_params_init(&dh_parameters))) {
929 close(server_socket);
930 gnutls_certificate_free_credentials(x509_credentials);
931 gnutls_priority_deinit(priority_cache);
932 gnutls_global_deinit();
933 set_server_error("Could not allocate Diffie-Hellman parameters: "
934 "%s", gnutls_strerror(error));
935 return -1;
937 if ((error = gnutls_dh_params_generate2(dh_parameters,
938 gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
939 GNUTLS_SEC_PARAM_LOW)))) {
940 close(server_socket);
941 gnutls_certificate_free_credentials(x509_credentials);
942 gnutls_priority_deinit(priority_cache);
943 gnutls_dh_params_deinit(dh_parameters);
944 gnutls_global_deinit();
945 set_server_error("Could not generate Diffie-Hellman parameters: %s",
946 gnutls_strerror(error));
947 return -1;
949 gnutls_certificate_set_dh_params(x509_credentials, dh_parameters);
950 /* XXX: dh_parameters are linked from x509_credentials now.
951 * Deinitialization must free x509_credentials before dh_parameters. */
952 if ((error = gnutls_session_ticket_key_generate(&ticket_key))) {
953 fprintf(stderr, "Could not generate TLS session ticket key: %s\n",
954 gnutls_strerror(error));
955 ticket_key.data = NULL;
959 *server_process = fork();
960 if (*server_process == -1) {
961 close(server_socket);
962 if (NULL != tls) {
963 gnutls_free(ticket_key.data);
964 gnutls_certificate_free_credentials(x509_credentials);
965 gnutls_priority_deinit(priority_cache);
966 gnutls_dh_params_deinit(dh_parameters);
967 gnutls_global_deinit();
969 set_server_error("Server could not been forked");
970 return -1;
973 if (*server_process == 0) {
974 int client_socket;
975 gnutls_session_t tls_session;
976 struct http_connection connection;
977 struct http_request *request = NULL;
978 http_error error;
979 int terminate = 0;
981 while (!terminate) {
982 if (NULL != tls) {
983 if ((error = gnutls_init(&tls_session, GNUTLS_SERVER))) {
984 set_server_error("Could not initialize TLS session: %s",
985 gnutls_strerror(error));
986 terminate = -1;
987 continue;
989 if ((error = gnutls_priority_set(tls_session,
990 priority_cache))) {
991 set_server_error(
992 "Could not set priorities to TLS session: %s",
993 gnutls_strerror(error));
994 terminate = -1;
995 gnutls_deinit(tls_session);
996 continue;
998 if ((error = gnutls_credentials_set(tls_session,
999 GNUTLS_CRD_CERTIFICATE, x509_credentials))) {
1000 set_server_error("Could not set X509 credentials to TLS "
1001 "session: %s", gnutls_strerror(error));
1002 terminate = -1;
1003 gnutls_deinit(tls_session);
1004 continue;
1006 if (NULL != ticket_key.data) {
1007 if ((error = gnutls_session_ticket_enable_server(
1008 tls_session, &ticket_key))) {
1009 fprintf(stderr, "Could not register ticket key to "
1010 "TLS session: %s\n", gnutls_strerror(error));
1013 /* XXX: Credentials are linked from session now.
1014 * Deinitializition must free session before x509_credentials.
1016 if (NULL != tls->client_name) {
1017 client_required_dn = tls->client_name;
1018 /* Require client certificate */
1019 gnutls_certificate_server_set_request(tls_session,
1020 GNUTLS_CERT_REQUIRE);
1021 /* And verify it in TLS handshake */
1022 gnutls_certificate_set_verify_function(x509_credentials,
1023 tls_verify_client);
1027 if (0 > (client_socket = accept(server_socket, NULL, NULL))) {
1028 terminate = -1;
1029 if (NULL != tls)
1030 gnutls_deinit(tls_session);
1031 continue;
1033 fprintf(stderr, "Connection accepted\n");
1034 connection.socket = client_socket;
1036 if (NULL == tls) {
1037 connection.recv_callback = recv_plain;
1038 connection.send_callback = send_plain;
1039 connection.callback_data = NULL;
1040 } else {
1041 connection.recv_callback = recv_tls;
1042 connection.send_callback = send_tls;
1043 connection.callback_data = tls_session;
1044 gnutls_transport_set_pull_function(tls_session, tls_pull);
1045 gnutls_transport_set_push_function(tls_session, tls_push);
1046 gnutls_transport_set_ptr2(tls_session,
1047 &connection, &connection);
1048 do {
1049 error = gnutls_handshake(tls_session);
1050 } while (error < 0 && !gnutls_error_is_fatal(error));
1051 if (error < 0) {
1052 fprintf(stderr, "TLS handshake failed: %s\n",
1053 gnutls_strerror(error));
1054 close(client_socket);
1055 gnutls_deinit(tls_session);
1056 continue;
1060 error = http_read_request(&connection, &request);
1061 if (error) {
1062 fprintf(stderr, "Error while reading request\n");
1063 if (error == HTTP_ERROR_CLIENT)
1064 http_send_response_400(&connection, "Error in request");
1065 else
1066 http_send_response_500(&connection,
1067 "Could not read request");
1068 close(client_socket);
1069 if (NULL != tls)
1070 gnutls_deinit(tls_session);
1071 continue;
1074 terminate = server_implementation(&connection, server_arguments,
1075 request);
1077 http_request_free(&request);
1078 close(client_socket);
1079 if (NULL != tls) {
1080 gnutls_deinit(tls_session);
1084 if (NULL != tls) {
1085 if (NULL != ticket_key.data) {
1086 gnutls_free(ticket_key.data);
1089 close(server_socket);
1090 free(authorization_cookie_value);
1091 exit(EXIT_SUCCESS);
1092 /* Does not return */
1095 return 0;
1099 /* Kill the server process.
1100 * Return 0. Return -1 if server could not been stopped. Return 1 if server
1101 * crashed. */
1102 int stop_server(pid_t server_process) {
1103 int status;
1104 if (server_process <= 0) {
1105 set_server_error("Invalid server PID to kill");
1106 return -1;
1108 if (-1 == kill(server_process, SIGTERM)) {
1109 set_server_error("Could not terminate server");
1110 return -1;
1112 if (-1 == waitpid(server_process, &status, 0)) {
1113 set_server_error("Could not wait for server termination");
1114 return -1;
1116 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
1117 set_server_error("Server terminated by signal %d violently",
1118 WTERMSIG(status));
1119 return 1;
1121 return 0;