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
;
40 /* Save pointer to static error message if not yet set */
41 void set_server_error(const char *message
) {
42 if (server_error
== NULL
) {
43 server_error
= message
;
48 /* Creates listening TCP socket on localhost.
49 * Returns the socket descriptor or -1. */
50 int listen_on_socket(void) {
52 struct addrinfo hints
;
53 struct addrinfo
*addresses
, *address
;
56 memset(&hints
, 0, sizeof(hints
));
57 hints
.ai_family
= AF_UNSPEC
;
58 hints
.ai_socktype
= SOCK_STREAM
;
59 retval
= getaddrinfo("localhost", NULL
, &hints
, &addresses
);
61 set_server_error("Could not resolve `localhost'");
65 for (address
= addresses
; address
!= NULL
; address
= address
->ai_next
) {
66 fd
= socket(address
->ai_family
, address
->ai_socktype
,
67 address
->ai_protocol
);
68 if (fd
== -1) continue;
70 retval
= bind(fd
, address
->ai_addr
, address
->ai_addrlen
);
71 if (retval
!= 0) continue;
73 retval
= listen(fd
, 0);
75 freeaddrinfo(addresses
);
80 freeaddrinfo(addresses
);
81 set_server_error("Could not start listening on TCP/localhost");
86 /* Format socket address as printable string.
87 * @return allocated string or NULL in case of error. */
88 char *socket2address(int socket
) {
89 struct sockaddr_storage storage
;
90 socklen_t length
= (socklen_t
) sizeof(storage
);
91 char host
[NI_MAXHOST
];
92 char service
[NI_MAXSERV
];
95 if (-1 == getsockname(socket
, (struct sockaddr
*)&storage
, &length
)) {
96 set_server_error("Could not get address of server socket");
100 if (0 != getnameinfo((struct sockaddr
*)&storage
, length
,
101 host
, sizeof(host
), service
, sizeof(service
),
102 NI_NUMERICHOST
|NI_NUMERICSERV
)) {
103 set_server_error("Could not resolve address of server socket");
107 if (-1 == test_asprintf(&address
,
108 (strchr(host
, ':') == NULL
) ? "%s:%s" : "[%s]:%s",
110 set_server_error("Could not format server address");
118 /* Process ISDS web service */
119 static void do_ws(int client_socket
,
120 const struct service_configuration
*ws_configuration
,
121 const struct http_request
*request
, const char *required_base_path
) {
122 char *end_point
= request
->uri
; /* Pointer to string in the request */
124 if (request
->method
!= HTTP_METHOD_POST
) {
125 http_send_response_400(client_socket
,
126 "Regular ISDS web service request must be POST");
130 if (required_base_path
!= NULL
) {
131 size_t required_base_path_length
= strlen(required_base_path
);
132 if (strncmp(end_point
, required_base_path
, required_base_path_length
)) {
133 http_send_response_400(client_socket
,
134 "Request sent to invalid path");
137 end_point
+= required_base_path_length
;
140 soap(client_socket
, ws_configuration
, request
->body
, request
->body_length
,
145 /* Do the server protocol.
146 * @server_socket is listening TCP socket of the server
147 * @server_arguments is pointer to structure:
148 * Never returns. Terminates by exit(). */
149 void server_basic_authentication(int server_socket
,
150 const void *server_arguments
) {
152 const struct arguments_basic_authentication
*arguments
=
153 (const struct arguments_basic_authentication
*) server_arguments
;
154 struct http_request
*request
= NULL
;
157 if (arguments
== NULL
) {
158 close(server_socket
);
162 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
163 fprintf(stderr
, "Connection accepted\n");
164 error
= http_read_request(client_socket
, &request
);
166 fprintf(stderr
, "Error while reading request\n");
167 if (error
== HTTP_ERROR_CLIENT
)
168 http_send_response_400(client_socket
, "Error in request");
170 http_send_response_500(client_socket
,
171 "Error while reading request");
172 close(client_socket
);
176 if (request
->method
== HTTP_METHOD_POST
) {
177 /* Only POST requests are used in Basic authentication mode */
178 if (arguments
->username
!= NULL
) {
179 if (http_client_authenticates(request
)) {
180 switch(http_authenticate_basic(request
,
181 arguments
->username
, arguments
->password
)) {
182 case HTTP_ERROR_SUCCESS
:
183 do_ws(client_socket
, arguments
->services
, request
,
186 case HTTP_ERROR_CLIENT
:
187 if (arguments
->isds_deviations
)
188 http_send_response_401_basic(client_socket
);
190 http_send_response_403(client_socket
);
193 http_send_response_500(client_socket
,
194 "Server error while verifying Basic "
198 http_send_response_401_basic(client_socket
);
201 do_ws(client_socket
, arguments
->services
, request
,
205 /* HTTP method unsupported per ISDS specification */
206 http_send_response_400(client_socket
,
207 "Only POST method is allowed");
209 http_request_free(&request
);
212 close(client_socket
);
215 close(server_socket
);
220 /* Process first phase of TOTP request */
221 static void do_as_sendsms(int client_socket
, const struct http_request
*request
,
222 const struct arguments_otp_authentication
*arguments
) {
223 if (arguments
== NULL
) {
224 http_send_response_500(client_socket
,
225 "Third argument of do_as_sendsms() is NULL");
229 if (request
->method
!= HTTP_METHOD_POST
) {
230 http_send_response_400(client_socket
,
231 "First phase TOTP request must be POST");
235 if (!http_client_authenticates(request
)) {
236 http_send_response_401_otp(client_socket
,
238 "authentication.error.userIsNotAuthenticated",
239 "Client did not send any authentication header");
243 switch(http_authenticate_basic(request
,
244 arguments
->username
, arguments
->password
)) {
245 case HTTP_ERROR_SUCCESS
: {
247 char *uri
= strstr(request
->uri
, "&uri=");
249 http_send_response_400(client_socket
,
250 "Missing uri parameter in Request URI");
254 /* Build new location for second OTP phase */
255 char *location
= NULL
;
256 if (-1 == test_asprintf(&location
, "%s%s", as_path_dontsendsms
, uri
)) {
257 http_send_response_500(client_socket
,
258 "Could not build new localtion for "
262 char *terminator
= strchr(uri
, '&');
263 if (NULL
!= terminator
)
264 location
[strlen(as_path_dontsendsms
) + (uri
- terminator
)] = '\0';
265 http_send_response_302_totp(client_socket
,
266 "authentication.info.totpSended",
267 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
272 case HTTP_ERROR_CLIENT
:
273 if (arguments
->isds_deviations
)
274 http_send_response_401_otp(client_socket
,
276 "authentication.error.userIsNotAuthenticated",
277 " Retry: Bad user name or password in first OTP phase.\r\n"
278 " This is very long header\r\n"
279 " which should span to more lines.\r\n"
280 " Surrounding LWS are meaning-less. ");
282 http_send_response_403(client_socket
);
285 http_send_response_500(client_socket
,
286 "Could not verify Basic authentication");
291 /* Return static string representation of HTTP OTP authentication method.
292 * Or NULL in case of error. */
293 static const char *auth_otp_method2string(enum auth_otp_method method
) {
295 case AUTH_OTP_HMAC
: return "hotp";
296 case AUTH_OTP_TIME
: return "totp";
297 default: return NULL
;
302 /* Process second phase of OTP request */
303 static void do_as_phase_two(int client_socket
, const struct http_request
*request
,
304 const struct arguments_otp_authentication
*arguments
) {
305 if (arguments
== NULL
) {
306 http_send_response_500(client_socket
,
307 "Third argument of do_as_phase_two() is NULL");
311 if (request
->method
!= HTTP_METHOD_POST
) {
312 http_send_response_400(client_socket
,
313 "Second phase OTP request must be POST");
317 if (!http_client_authenticates(request
)) {
318 http_send_response_401_otp(client_socket
,
319 auth_otp_method2string(arguments
->method
),
320 "authentication.error.userIsNotAuthenticated",
321 "Client did not send any authentication header");
325 switch(http_authenticate_otp(request
,
326 arguments
->username
, arguments
->password
, arguments
->otp
)) {
327 case HTTP_ERROR_SUCCESS
: {
329 char *uri
= strstr(request
->uri
, "&uri=");
331 http_send_response_400(client_socket
,
332 "Missing uri parameter in Request URI");
336 /* Build new location for final request */
337 char *location
= NULL
;
338 if (NULL
== (location
= strdup(uri
))) {
339 http_send_response_500(client_socket
,
340 "Could not build new location for final request");
343 char *terminator
= strchr(location
, '&');
344 if (NULL
!= terminator
)
346 /* Generate pseudo-random cookie value. This is to prevent
347 * client from reusing the cookie accidentally. We use the
348 * same seed to get reproducible tests. */
349 if (-1 == test_asprintf(&authorization_cookie_value
, "%d",
351 http_send_response_500(client_socket
,
352 "Could not generate cookie value");
356 /* XXX: Add Path parameter to cookie, otherwise
357 * different paths will not match.
358 * FIXME: Domain argument does not work with cURL. Report a bug. */
359 http_send_response_302_cookie(client_socket
,
360 authorization_cookie_name
,
361 authorization_cookie_value
,
362 /*http_find_host(request)*/NULL
,
368 case HTTP_ERROR_CLIENT
:
369 if (arguments
->isds_deviations
)
370 http_send_response_401_otp(client_socket
,
371 auth_otp_method2string(arguments
->method
),
372 "authentication.error.userIsNotAuthenticated",
373 " Retry: Bad user name or password in second OTP phase.\r\n"
374 " This is very long header\r\n"
375 " which should span to more lines.\r\n"
376 " Surrounding LWS are meaning-less. ");
378 http_send_response_403(client_socket
);
381 http_send_response_500(client_socket
,
382 "Could not verify OTP authentication");
387 /* Process ASWS for changing OTP password requests */
388 /* FIXME: The ASWS URI hosts two services: for sending TOTP code for password
389 * change and for changing OTP password. The problem is the former one is
390 * basic-authenticated, the later one is otp-authenticated. But we cannot
391 * decide which authentication to enforce without understadning request body.
392 * I will just try both of them to choose the service.
393 * But I hope official server implementation does it in more clever way. */
394 static void do_asws(int client_socket
, const struct http_request
*request
,
395 const struct arguments_otp_authentication
*arguments
) {
397 if (arguments
== NULL
) {
398 http_send_response_500(client_socket
,
399 "Third argument of do_asws() is NULL");
403 if (request
->method
!= HTTP_METHOD_POST
) {
404 http_send_response_400(client_socket
,
405 "ASWS request must be POST");
409 if (!http_client_authenticates(request
)) {
410 http_send_response_401_otp(client_socket
,
411 auth_otp_method2string(arguments
->method
),
412 "authentication.error.userIsNotAuthenticated",
413 "Client did not send any authentication header");
418 error
= http_authenticate_otp(request
,
419 arguments
->username
, arguments
->password
, arguments
->otp
);
420 if (HTTP_ERROR_SUCCESS
== error
) {
421 /* This will be request for password change because OTP
422 * authentication succeeded. */
423 do_ws(client_socket
, arguments
->services
, request
, NULL
);
427 if (AUTH_OTP_TIME
== arguments
->method
) {
429 error
= http_authenticate_basic(request
,
430 arguments
->username
, arguments
->password
);
431 if (HTTP_ERROR_SUCCESS
== error
) {
432 /* This will be request for time code for password change because
433 * basic authentication succeeded. */
434 do_ws(client_socket
, arguments
->services
, request
, NULL
);
439 if (HTTP_ERROR_CLIENT
== error
) {
440 if (arguments
->isds_deviations
)
441 http_send_response_401_otp(client_socket
,
442 auth_otp_method2string(arguments
->method
),
443 "authentication.error.userIsNotAuthenticated",
444 " Retry: Bad user name or password in Authorization.\r\n"
445 " This is very long header\r\n"
446 " which should span to more lines.\r\n"
447 " Surrounding LWS are meaning-less. ");
449 http_send_response_403(client_socket
);
453 http_send_response_500(client_socket
,
454 "Could not verify OTP authentication");
458 /* Process OTP session cookie invalidation request */
459 static void do_as_logout(int client_socket
, const struct http_request
*request
,
460 const struct arguments_otp_authentication
*arguments
) {
461 if (arguments
== NULL
) {
462 http_send_response_500(client_socket
,
463 "Third argument of do_as_logout() is NULL");
467 if (request
->method
!= HTTP_METHOD_GET
) {
468 http_send_response_400(client_socket
,
469 "OTP cookie invalidation request must be GET");
473 const char *received_cookie
=
474 http_find_cookie(request
, authorization_cookie_name
);
476 if (authorization_cookie_value
== NULL
|| received_cookie
== NULL
||
477 strcmp(authorization_cookie_value
, received_cookie
)) {
478 http_send_response_403(client_socket
);
482 /* XXX: Add Path parameter to cookie, otherwise
483 * different paths will not match.
484 * FIXME: Domain argument does not work with cURL. Report a bug. */
485 http_send_response_200_cookie(client_socket
,
486 authorization_cookie_name
,
488 /*http_find_host(request)*/ NULL
,
494 /* Process ISDS WS ping authorized by cookie */
495 static void do_ws_with_cookie(int client_socket
,
496 const struct http_request
*request
,
497 const struct arguments_otp_authentication
*arguments
,
498 const char *valid_base_path
) {
499 const char *received_cookie
=
500 http_find_cookie(request
, authorization_cookie_name
);
502 if (authorization_cookie_value
!= NULL
&& received_cookie
!= NULL
&&
503 !strcmp(authorization_cookie_value
, received_cookie
))
504 do_ws(client_socket
, arguments
->services
, request
, valid_base_path
);
506 http_send_response_403(client_socket
);
510 /* Do the server protocol with OTP authentication.
511 * @server_socket is listening TCP socket of the server
512 * @server_arguments is pointer to structure arguments_otp_authentication. It
513 * selects OTP method to enable.
514 * Never returns. Terminates by exit(). */
515 void server_otp_authentication(int server_socket
,
516 const void *server_arguments
) {
518 const struct arguments_otp_authentication
*arguments
=
519 (const struct arguments_otp_authentication
*) server_arguments
;
520 struct http_request
*request
= NULL
;
523 if (arguments
== NULL
) {
524 close(server_socket
);
528 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
529 fprintf(stderr
, "Connection accepted\n");
530 error
= http_read_request(client_socket
, &request
);
532 fprintf(stderr
, "Error while reading request\n");
533 if (error
== HTTP_ERROR_CLIENT
)
534 http_send_response_400(client_socket
, "Error in request");
536 http_send_response_500(client_socket
, "Could not read request");
537 close(client_socket
);
541 if (arguments
->username
!= NULL
) {
542 if (arguments
->method
== AUTH_OTP_HMAC
&&
543 !strncmp(request
->uri
, as_path_hotp
, strlen(as_path_hotp
))) {
544 do_as_phase_two(client_socket
, request
, arguments
);
545 } else if (arguments
->method
== AUTH_OTP_TIME
&&
546 !strncmp(request
->uri
, as_path_sendsms
,
547 strlen(as_path_sendsms
))) {
548 do_as_sendsms(client_socket
, request
, arguments
);
549 } else if (arguments
->method
== AUTH_OTP_TIME
&&
550 !strncmp(request
->uri
, as_path_dontsendsms
,
551 strlen(as_path_dontsendsms
))) {
552 do_as_phase_two(client_socket
, request
, arguments
);
553 } else if (!strncmp(request
->uri
, as_path_logout
,
554 strlen(as_path_logout
))) {
555 do_as_logout(client_socket
, request
, arguments
);
556 } else if (!strcmp(request
->uri
, asws_path
)) {
557 do_asws(client_socket
, request
, arguments
);
558 } else if (!strcmp(request
->uri
, ws_path
)) {
559 do_ws_with_cookie(client_socket
, request
, arguments
,
562 http_send_response_400(client_socket
,
563 "Unknown path for OTP authenticating service");
566 if (!strcmp(request
->uri
, ws_path
)) {
567 do_ws(client_socket
, arguments
->services
, request
,
570 http_send_response_400(client_socket
,
571 "Unknown path for OTP authenticating service");
574 http_request_free(&request
);
577 close(client_socket
);
580 close(server_socket
);
581 free(authorization_cookie_value
);
586 /* Implementation of server that is out of order.
587 * It always sends back SOAP Fault with HTTP error 503.
588 * @server_socket is listening TCP socket of the server
589 * @server_arguments is ununsed pointer
590 * Never returns. Terminates by exit(). */
591 void server_out_of_order(int server_socket
,
592 const void *server_arguments
) {
594 struct http_request
*request
= NULL
;
596 const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
597 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>";
599 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
600 fprintf(stderr
, "Connection accepted\n");
601 error
= http_read_request(client_socket
, &request
);
603 fprintf(stderr
, "Error while reading request\n");
604 if (error
== HTTP_ERROR_CLIENT
)
605 http_send_response_400(client_socket
, "Error in request");
606 close(client_socket
);
610 http_send_response_503(client_socket
, fault
, strlen(fault
),
612 http_request_free(&request
);
614 close(client_socket
);
617 close(server_socket
);
622 /* Start sever in separate process.
623 * @server_process is PID of forked server
624 * @server_address is automatically allocated TCP address of listening server
625 * @server_implementation points to kind of server to implement. Valid values
626 * are addresses of server_basic_authentication(),
627 * server_otp_authentication(), or server_out_of_order().
628 * @server_arguments is pointer to argument pass to @server_implementation. It
630 * @username sets required user name server has to require. Set NULL to
631 * disable HTTP authentication.
632 * @password sets required password server has to require
634 * @isds_deviations is flag to set conformance level. If false, server is
635 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
636 * specification. Otherwise server mimics real ISDS implementation as much
638 * @tls sets TLS layer. Pass NULL for plain HTTP.
639 * @return -1 in case of error. */
640 int start_server(pid_t
*server_process
, char **server_address
,
641 void (*server_implementation
)(int, const void *),
642 const void *server_arguments
, const struct tls_authentication
*tls
) {
645 if (server_address
== NULL
) {
646 set_server_error("start_server(): Got invalid server_address pointer");
649 *server_address
= NULL
;
651 if (server_process
== NULL
) {
652 set_server_error("start_server(): Got invalid server_process pointer");
656 server_socket
= listen_on_socket();
657 if (server_socket
== -1) {
658 set_server_error("Could not create listening socket");
662 *server_address
= socket2address(server_socket
);
663 if (*server_address
== NULL
) {
664 close(server_socket
);
665 set_server_error("Could not format address of listening address");
669 *server_process
= fork();
670 if (*server_process
== -1) {
671 close(server_socket
);
672 set_server_error("Server could not been forked");
676 if (*server_process
== 0) {
677 server_implementation(server_socket
, server_arguments
);
678 /* Does not return */
685 /* Kill the server process.
686 * Return -1 in case of error. */
687 int stop_server(pid_t server_process
) {
688 if (server_process
<= 0) {
689 set_server_error("Invalid server PID to kill");
692 if (-1 == kill(server_process
, SIGTERM
)) {
693 set_server_error("Could not terminate server");
696 if (-1 == waitpid(server_process
, NULL
, 0)) {
697 set_server_error("Could not wait for server termination");