2 #define _POSIX_SOURCE /* For getaddrinfo(3) */
6 #define _BSD_SOURCE /* For NI_MAXHOST */
9 #include "../test-tools.h"
16 #include <sys/types.h>
17 #include <sys/socket.h>
23 #include <gnutls/gnutls.h>
25 const char *server_error
= NULL
;
27 static const char *as_path_hotp
= "/as/processLogin?type=hotp&uri=";
28 static const char *as_path_sendsms
= "/as/processLogin?type=totp&sendSms=true&uri=";
29 static const char *as_path_dontsendsms
= "/as/processLogin?type=totp&uri=";
30 static const char *as_path_logout
= "/as/processLogout?uri=";
31 static const char *asws_path
= "/asws/changePassword";
32 static const char *ws_path
= "/apps/DS/dz";
34 static const char *ws_base_path_basic
= "/";
35 static const char *ws_base_path_otp
= "/apps/";
37 static const char *authorization_cookie_name
= "IPCZ-X-COOKIE";
38 static char *authorization_cookie_value
= NULL
;
41 static gnutls_certificate_credentials_t x509_credentials
;
42 static gnutls_priority_t priority_cache
;
43 static gnutls_dh_params_t dh_parameters
;
45 /* Save pointer to static error message if not yet set */
46 void set_server_error(const char *message
) {
47 if (server_error
== NULL
) {
48 server_error
= message
;
53 /* Creates listening TCP socket on localhost.
54 * Returns the socket descriptor or -1. */
55 int listen_on_socket(void) {
57 struct addrinfo hints
;
58 struct addrinfo
*addresses
, *address
;
61 memset(&hints
, 0, sizeof(hints
));
62 hints
.ai_family
= AF_UNSPEC
;
63 hints
.ai_socktype
= SOCK_STREAM
;
64 retval
= getaddrinfo("localhost", NULL
, &hints
, &addresses
);
66 set_server_error("Could not resolve `localhost'");
70 for (address
= addresses
; address
!= NULL
; address
= address
->ai_next
) {
71 fd
= socket(address
->ai_family
, address
->ai_socktype
,
72 address
->ai_protocol
);
73 if (fd
== -1) continue;
75 retval
= bind(fd
, address
->ai_addr
, address
->ai_addrlen
);
76 if (retval
!= 0) continue;
78 retval
= listen(fd
, 0);
80 freeaddrinfo(addresses
);
85 freeaddrinfo(addresses
);
86 set_server_error("Could not start listening on TCP/localhost");
91 /* Format socket address as printable string.
92 * @return allocated string or NULL in case of error. */
93 char *socket2address(int socket
) {
94 struct sockaddr_storage storage
;
95 socklen_t length
= (socklen_t
) sizeof(storage
);
96 char host
[NI_MAXHOST
];
97 char service
[NI_MAXSERV
];
100 if (-1 == getsockname(socket
, (struct sockaddr
*)&storage
, &length
)) {
101 set_server_error("Could not get address of server socket");
105 if (0 != getnameinfo((struct sockaddr
*)&storage
, length
,
106 host
, sizeof(host
), service
, sizeof(service
),
107 NI_NUMERICHOST
|NI_NUMERICSERV
)) {
108 set_server_error("Could not resolve address of server socket");
112 if (-1 == test_asprintf(&address
,
113 (strchr(host
, ':') == NULL
) ? "%s:%s" : "[%s]:%s",
115 set_server_error("Could not format server address");
123 /* Process ISDS web service */
124 static void do_ws(int client_socket
,
125 const struct service_configuration
*ws_configuration
,
126 const struct http_request
*request
, const char *required_base_path
) {
127 char *end_point
= request
->uri
; /* Pointer to string in the request */
129 if (request
->method
!= HTTP_METHOD_POST
) {
130 http_send_response_400(client_socket
,
131 "Regular ISDS web service request must be POST");
135 if (required_base_path
!= NULL
) {
136 size_t required_base_path_length
= strlen(required_base_path
);
137 if (strncmp(end_point
, required_base_path
, required_base_path_length
)) {
138 http_send_response_400(client_socket
,
139 "Request sent to invalid path");
142 end_point
+= required_base_path_length
;
145 soap(client_socket
, ws_configuration
, request
->body
, request
->body_length
,
150 /* Do the server protocol.
151 * @server_socket is listening TCP socket of the server
152 * @server_arguments is pointer to structure:
153 * Never returns. Terminates by exit(). */
154 void server_basic_authentication(int server_socket
,
155 const void *server_arguments
) {
157 const struct arguments_basic_authentication
*arguments
=
158 (const struct arguments_basic_authentication
*) server_arguments
;
159 struct http_request
*request
= NULL
;
162 if (arguments
== NULL
) {
163 close(server_socket
);
167 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
168 fprintf(stderr
, "Connection accepted\n");
169 error
= http_read_request(client_socket
, &request
);
171 fprintf(stderr
, "Error while reading request\n");
172 if (error
== HTTP_ERROR_CLIENT
)
173 http_send_response_400(client_socket
, "Error in request");
175 http_send_response_500(client_socket
,
176 "Error while reading request");
177 close(client_socket
);
181 if (request
->method
== HTTP_METHOD_POST
) {
182 /* Only POST requests are used in Basic authentication mode */
183 if (arguments
->username
!= NULL
) {
184 if (http_client_authenticates(request
)) {
185 switch(http_authenticate_basic(request
,
186 arguments
->username
, arguments
->password
)) {
187 case HTTP_ERROR_SUCCESS
:
188 do_ws(client_socket
, arguments
->services
, request
,
191 case HTTP_ERROR_CLIENT
:
192 if (arguments
->isds_deviations
)
193 http_send_response_401_basic(client_socket
);
195 http_send_response_403(client_socket
);
198 http_send_response_500(client_socket
,
199 "Server error while verifying Basic "
203 http_send_response_401_basic(client_socket
);
206 do_ws(client_socket
, arguments
->services
, request
,
210 /* HTTP method unsupported per ISDS specification */
211 http_send_response_400(client_socket
,
212 "Only POST method is allowed");
214 http_request_free(&request
);
217 close(client_socket
);
220 close(server_socket
);
225 /* Process first phase of TOTP request */
226 static void do_as_sendsms(int client_socket
, const struct http_request
*request
,
227 const struct arguments_otp_authentication
*arguments
) {
228 if (arguments
== NULL
) {
229 http_send_response_500(client_socket
,
230 "Third argument of do_as_sendsms() is NULL");
234 if (request
->method
!= HTTP_METHOD_POST
) {
235 http_send_response_400(client_socket
,
236 "First phase TOTP request must be POST");
240 if (!http_client_authenticates(request
)) {
241 http_send_response_401_otp(client_socket
,
243 "authentication.error.userIsNotAuthenticated",
244 "Client did not send any authentication header");
248 switch(http_authenticate_basic(request
,
249 arguments
->username
, arguments
->password
)) {
250 case HTTP_ERROR_SUCCESS
: {
252 char *uri
= strstr(request
->uri
, "&uri=");
254 http_send_response_400(client_socket
,
255 "Missing uri parameter in Request URI");
259 /* Build new location for second OTP phase */
260 char *location
= NULL
;
261 if (-1 == test_asprintf(&location
, "%s%s", as_path_dontsendsms
, uri
)) {
262 http_send_response_500(client_socket
,
263 "Could not build new localtion for "
267 char *terminator
= strchr(uri
, '&');
268 if (NULL
!= terminator
)
269 location
[strlen(as_path_dontsendsms
) + (uri
- terminator
)] = '\0';
270 http_send_response_302_totp(client_socket
,
271 "authentication.info.totpSended",
272 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
277 case HTTP_ERROR_CLIENT
:
278 if (arguments
->isds_deviations
)
279 http_send_response_401_otp(client_socket
,
281 "authentication.error.userIsNotAuthenticated",
282 " Retry: Bad user name or password in first OTP phase.\r\n"
283 " This is very long header\r\n"
284 " which should span to more lines.\r\n"
285 " Surrounding LWS are meaning-less. ");
287 http_send_response_403(client_socket
);
290 http_send_response_500(client_socket
,
291 "Could not verify Basic authentication");
296 /* Return static string representation of HTTP OTP authentication method.
297 * Or NULL in case of error. */
298 static const char *auth_otp_method2string(enum auth_otp_method method
) {
300 case AUTH_OTP_HMAC
: return "hotp";
301 case AUTH_OTP_TIME
: return "totp";
302 default: return NULL
;
307 /* Process second phase of OTP request */
308 static void do_as_phase_two(int client_socket
, const struct http_request
*request
,
309 const struct arguments_otp_authentication
*arguments
) {
310 if (arguments
== NULL
) {
311 http_send_response_500(client_socket
,
312 "Third argument of do_as_phase_two() is NULL");
316 if (request
->method
!= HTTP_METHOD_POST
) {
317 http_send_response_400(client_socket
,
318 "Second phase OTP request must be POST");
322 if (!http_client_authenticates(request
)) {
323 http_send_response_401_otp(client_socket
,
324 auth_otp_method2string(arguments
->method
),
325 "authentication.error.userIsNotAuthenticated",
326 "Client did not send any authentication header");
330 switch(http_authenticate_otp(request
,
331 arguments
->username
, arguments
->password
, arguments
->otp
)) {
332 case HTTP_ERROR_SUCCESS
: {
334 char *uri
= strstr(request
->uri
, "&uri=");
336 http_send_response_400(client_socket
,
337 "Missing uri parameter in Request URI");
341 /* Build new location for final request */
342 char *location
= NULL
;
343 if (NULL
== (location
= strdup(uri
))) {
344 http_send_response_500(client_socket
,
345 "Could not build new location for final request");
348 char *terminator
= strchr(location
, '&');
349 if (NULL
!= terminator
)
351 /* Generate pseudo-random cookie value. This is to prevent
352 * client from reusing the cookie accidentally. We use the
353 * same seed to get reproducible tests. */
354 if (-1 == test_asprintf(&authorization_cookie_value
, "%d",
356 http_send_response_500(client_socket
,
357 "Could not generate cookie value");
361 /* XXX: Add Path parameter to cookie, otherwise
362 * different paths will not match.
363 * FIXME: Domain argument does not work with cURL. Report a bug. */
364 http_send_response_302_cookie(client_socket
,
365 authorization_cookie_name
,
366 authorization_cookie_value
,
367 /*http_find_host(request)*/NULL
,
373 case HTTP_ERROR_CLIENT
:
374 if (arguments
->isds_deviations
)
375 http_send_response_401_otp(client_socket
,
376 auth_otp_method2string(arguments
->method
),
377 "authentication.error.userIsNotAuthenticated",
378 " Retry: Bad user name or password in second OTP phase.\r\n"
379 " This is very long header\r\n"
380 " which should span to more lines.\r\n"
381 " Surrounding LWS are meaning-less. ");
383 http_send_response_403(client_socket
);
386 http_send_response_500(client_socket
,
387 "Could not verify OTP authentication");
392 /* Process ASWS for changing OTP password requests */
393 /* FIXME: The ASWS URI hosts two services: for sending TOTP code for password
394 * change and for changing OTP password. The problem is the former one is
395 * basic-authenticated, the later one is otp-authenticated. But we cannot
396 * decide which authentication to enforce without understadning request body.
397 * I will just try both of them to choose the service.
398 * But I hope official server implementation does it in more clever way. */
399 static void do_asws(int client_socket
, const struct http_request
*request
,
400 const struct arguments_otp_authentication
*arguments
) {
402 if (arguments
== NULL
) {
403 http_send_response_500(client_socket
,
404 "Third argument of do_asws() is NULL");
408 if (request
->method
!= HTTP_METHOD_POST
) {
409 http_send_response_400(client_socket
,
410 "ASWS request must be POST");
414 if (!http_client_authenticates(request
)) {
415 http_send_response_401_otp(client_socket
,
416 auth_otp_method2string(arguments
->method
),
417 "authentication.error.userIsNotAuthenticated",
418 "Client did not send any authentication header");
423 error
= http_authenticate_otp(request
,
424 arguments
->username
, arguments
->password
, arguments
->otp
);
425 if (HTTP_ERROR_SUCCESS
== error
) {
426 /* This will be request for password change because OTP
427 * authentication succeeded. */
428 do_ws(client_socket
, arguments
->services
, request
, NULL
);
432 if (AUTH_OTP_TIME
== arguments
->method
) {
434 error
= http_authenticate_basic(request
,
435 arguments
->username
, arguments
->password
);
436 if (HTTP_ERROR_SUCCESS
== error
) {
437 /* This will be request for time code for password change because
438 * basic authentication succeeded. */
439 do_ws(client_socket
, arguments
->services
, request
, NULL
);
444 if (HTTP_ERROR_CLIENT
== error
) {
445 if (arguments
->isds_deviations
)
446 http_send_response_401_otp(client_socket
,
447 auth_otp_method2string(arguments
->method
),
448 "authentication.error.userIsNotAuthenticated",
449 " Retry: Bad user name or password in Authorization.\r\n"
450 " This is very long header\r\n"
451 " which should span to more lines.\r\n"
452 " Surrounding LWS are meaning-less. ");
454 http_send_response_403(client_socket
);
458 http_send_response_500(client_socket
,
459 "Could not verify OTP authentication");
463 /* Process OTP session cookie invalidation request */
464 static void do_as_logout(int client_socket
, const struct http_request
*request
,
465 const struct arguments_otp_authentication
*arguments
) {
466 if (arguments
== NULL
) {
467 http_send_response_500(client_socket
,
468 "Third argument of do_as_logout() is NULL");
472 if (request
->method
!= HTTP_METHOD_GET
) {
473 http_send_response_400(client_socket
,
474 "OTP cookie invalidation request must be GET");
478 const char *received_cookie
=
479 http_find_cookie(request
, authorization_cookie_name
);
481 if (authorization_cookie_value
== NULL
|| received_cookie
== NULL
||
482 strcmp(authorization_cookie_value
, received_cookie
)) {
483 http_send_response_403(client_socket
);
487 /* XXX: Add Path parameter to cookie, otherwise
488 * different paths will not match.
489 * FIXME: Domain argument does not work with cURL. Report a bug. */
490 http_send_response_200_cookie(client_socket
,
491 authorization_cookie_name
,
493 /*http_find_host(request)*/ NULL
,
499 /* Process ISDS WS ping authorized by cookie */
500 static void do_ws_with_cookie(int client_socket
,
501 const struct http_request
*request
,
502 const struct arguments_otp_authentication
*arguments
,
503 const char *valid_base_path
) {
504 const char *received_cookie
=
505 http_find_cookie(request
, authorization_cookie_name
);
507 if (authorization_cookie_value
!= NULL
&& received_cookie
!= NULL
&&
508 !strcmp(authorization_cookie_value
, received_cookie
))
509 do_ws(client_socket
, arguments
->services
, request
, valid_base_path
);
511 http_send_response_403(client_socket
);
515 /* Do the server protocol with OTP authentication.
516 * @server_socket is listening TCP socket of the server
517 * @server_arguments is pointer to structure arguments_otp_authentication. It
518 * selects OTP method to enable.
519 * Never returns. Terminates by exit(). */
520 void server_otp_authentication(int server_socket
,
521 const void *server_arguments
) {
523 const struct arguments_otp_authentication
*arguments
=
524 (const struct arguments_otp_authentication
*) server_arguments
;
525 struct http_request
*request
= NULL
;
528 if (arguments
== NULL
) {
529 close(server_socket
);
533 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
534 fprintf(stderr
, "Connection accepted\n");
535 error
= http_read_request(client_socket
, &request
);
537 fprintf(stderr
, "Error while reading request\n");
538 if (error
== HTTP_ERROR_CLIENT
)
539 http_send_response_400(client_socket
, "Error in request");
541 http_send_response_500(client_socket
, "Could not read request");
542 close(client_socket
);
546 if (arguments
->username
!= NULL
) {
547 if (arguments
->method
== AUTH_OTP_HMAC
&&
548 !strncmp(request
->uri
, as_path_hotp
, strlen(as_path_hotp
))) {
549 do_as_phase_two(client_socket
, request
, arguments
);
550 } else if (arguments
->method
== AUTH_OTP_TIME
&&
551 !strncmp(request
->uri
, as_path_sendsms
,
552 strlen(as_path_sendsms
))) {
553 do_as_sendsms(client_socket
, request
, arguments
);
554 } else if (arguments
->method
== AUTH_OTP_TIME
&&
555 !strncmp(request
->uri
, as_path_dontsendsms
,
556 strlen(as_path_dontsendsms
))) {
557 do_as_phase_two(client_socket
, request
, arguments
);
558 } else if (!strncmp(request
->uri
, as_path_logout
,
559 strlen(as_path_logout
))) {
560 do_as_logout(client_socket
, request
, arguments
);
561 } else if (!strcmp(request
->uri
, asws_path
)) {
562 do_asws(client_socket
, request
, arguments
);
563 } else if (!strcmp(request
->uri
, ws_path
)) {
564 do_ws_with_cookie(client_socket
, request
, arguments
,
567 http_send_response_400(client_socket
,
568 "Unknown path for OTP authenticating service");
571 if (!strcmp(request
->uri
, ws_path
)) {
572 do_ws(client_socket
, arguments
->services
, request
,
575 http_send_response_400(client_socket
,
576 "Unknown path for OTP authenticating service");
579 http_request_free(&request
);
582 close(client_socket
);
585 close(server_socket
);
586 free(authorization_cookie_value
);
591 /* Implementation of server that is out of order.
592 * It always sends back SOAP Fault with HTTP error 503.
593 * @server_socket is listening TCP socket of the server
594 * @server_arguments is ununsed pointer
595 * Never returns. Terminates by exit(). */
596 void server_out_of_order(int server_socket
,
597 const void *server_arguments
) {
599 struct http_request
*request
= NULL
;
601 const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
602 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>";
604 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
605 fprintf(stderr
, "Connection accepted\n");
606 error
= http_read_request(client_socket
, &request
);
608 fprintf(stderr
, "Error while reading request\n");
609 if (error
== HTTP_ERROR_CLIENT
)
610 http_send_response_400(client_socket
, "Error in request");
611 close(client_socket
);
615 http_send_response_503(client_socket
, fault
, strlen(fault
),
617 http_request_free(&request
);
619 close(client_socket
);
622 close(server_socket
);
627 /* Start sever in separate process.
628 * @server_process is PID of forked server
629 * @server_address is automatically allocated TCP address of listening server
630 * @server_implementation points to kind of server to implement. Valid values
631 * are addresses of server_basic_authentication(),
632 * server_otp_authentication(), or server_out_of_order().
633 * @server_arguments is pointer to argument pass to @server_implementation. It
635 * @username sets required user name server has to require. Set NULL to
636 * disable HTTP authentication.
637 * @password sets required password server has to require
639 * @isds_deviations is flag to set conformance level. If false, server is
640 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
641 * specification. Otherwise server mimics real ISDS implementation as much
643 * @tls sets TLS layer. Pass NULL for plain HTTP.
644 * @return -1 in case of error. */
645 int start_server(pid_t
*server_process
, char **server_address
,
646 void (*server_implementation
)(int, const void *),
647 const void *server_arguments
, const struct tls_authentication
*tls
) {
650 if (server_address
== NULL
) {
651 set_server_error("start_server(): Got invalid server_address pointer");
654 *server_address
= NULL
;
656 if (server_process
== NULL
) {
657 set_server_error("start_server(): Got invalid server_process pointer");
661 server_socket
= listen_on_socket();
662 if (server_socket
== -1) {
663 set_server_error("Could not create listening socket");
667 *server_address
= socket2address(server_socket
);
668 if (*server_address
== NULL
) {
669 close(server_socket
);
670 set_server_error("Could not format address of listening address");
675 if (gnutls_global_init()) {
676 close(server_socket
);
677 set_server_error("Could not initialize GnuTLS");
680 if (gnutls_certificate_allocate_credentials(&x509_credentials
)) {
681 close(server_socket
);
682 gnutls_global_deinit();
683 set_server_error("Could not allocate X.509 credentials");
686 if (gnutls_certificate_set_x509_trust_file(x509_credentials
,
687 tls
->authority_certificate
, GNUTLS_X509_FMT_PEM
)) {
688 close(server_socket
);
689 gnutls_certificate_free_credentials(x509_credentials
);
690 gnutls_global_deinit();
691 set_server_error("Could not load authority certificate");
694 if (gnutls_certificate_set_x509_key_file(x509_credentials
,
695 tls
->server_certificate
, tls
->server_key
,
696 GNUTLS_X509_FMT_PEM
)) {
697 close(server_socket
);
698 gnutls_certificate_free_credentials(x509_credentials
);
699 gnutls_global_deinit();
700 set_server_error("Could not load server certificate or "
704 if (gnutls_priority_init(&priority_cache
,
705 "PERFORMANCE:%SERVER_PRECEDENCE", NULL
)) {
706 close(server_socket
);
707 gnutls_certificate_free_credentials(x509_credentials
);
708 gnutls_global_deinit();
709 set_server_error("Could not set TLS algorithm preferences");
712 /* XXX: priority_cache is linked from x509_credentials now.
713 * Deinitialization must free x509_credentials before priority_cache. */
715 if (gnutls_dh_params_init(&dh_parameters
)) {
716 close(server_socket
);
717 gnutls_certificate_free_credentials(x509_credentials
);
718 gnutls_priority_deinit(priority_cache
);
719 gnutls_global_deinit();
720 set_server_error("Could not allocate Diffie-Hellman parameters");
723 if (gnutls_dh_params_generate2(dh_parameters
,
724 gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH
,
725 GNUTLS_SEC_PARAM_LOW
))) {
726 close(server_socket
);
727 gnutls_certificate_free_credentials(x509_credentials
);
728 gnutls_priority_deinit(priority_cache
);
729 gnutls_dh_params_deinit(dh_parameters
);
730 gnutls_global_deinit();
731 set_server_error("Could not generate Diffie-Hellman parameters");
734 gnutls_certificate_set_dh_params(x509_credentials
, dh_parameters
);
735 /* XXX: dh_parameters are linked from x509_credentials now.
736 * Deinitialization must free x509_credentials before dh_parameters. */
739 *server_process
= fork();
740 if (*server_process
== -1) {
741 close(server_socket
);
743 gnutls_certificate_free_credentials(x509_credentials
);
744 gnutls_global_deinit();
746 set_server_error("Server could not been forked");
750 if (*server_process
== 0) {
751 server_implementation(server_socket
, server_arguments
);
752 /* Does not return */
759 /* Kill the server process.
760 * Return -1 in case of error. */
761 int stop_server(pid_t server_process
) {
762 if (server_process
<= 0) {
763 set_server_error("Invalid server PID to kill");
766 if (-1 == kill(server_process
, SIGTERM
)) {
767 set_server_error("Could not terminate server");
770 if (-1 == waitpid(server_process
, NULL
, 0)) {
771 set_server_error("Could not wait for server termination");