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>
24 const char *server_error
= NULL
;
26 static const char *as_path_hotp
= "/as/processLogin?type=hotp&uri=";
27 static const char *as_path_sendsms
= "/as/processLogin?type=totp&sendSms=true&uri=";
28 static const char *as_path_dontsendsms
= "/as/processLogin?type=totp&uri=";
29 static const char *as_path_logout
= "/as/processLogout?uri=";
30 static const char *ws_path
= "/apps/DS/dz";
32 static const char *ws_base_path_basic
= "/";
33 static const char *ws_base_path_otp
= "/apps/";
35 static const char *authorization_cookie_name
= "IPCZ-X-COOKIE";
36 static char *authorization_cookie_value
= NULL
;
38 /* Save pointer to static error message if not yet set */
39 void set_server_error(const char *message
) {
40 if (server_error
== NULL
) {
41 server_error
= message
;
46 /* Creates listening TCP socket on localhost.
47 * Returns the socket descriptor or -1. */
48 int listen_on_socket(void) {
50 struct addrinfo hints
;
51 struct addrinfo
*addresses
, *address
;
54 memset(&hints
, 0, sizeof(hints
));
55 hints
.ai_family
= AF_UNSPEC
;
56 hints
.ai_socktype
= SOCK_STREAM
;
57 retval
= getaddrinfo("localhost", NULL
, &hints
, &addresses
);
59 set_server_error("Could not resolve `localhost'");
63 for (address
= addresses
; address
!= NULL
; address
= address
->ai_next
) {
64 fd
= socket(address
->ai_family
, address
->ai_socktype
,
65 address
->ai_protocol
);
66 if (fd
== -1) continue;
68 retval
= bind(fd
, address
->ai_addr
, address
->ai_addrlen
);
69 if (retval
!= 0) continue;
71 retval
= listen(fd
, 0);
73 freeaddrinfo(addresses
);
78 freeaddrinfo(addresses
);
79 set_server_error("Could not start listening on TCP/localhost");
84 /* Format socket address as printable string.
85 * @return allocated string or NULL in case of error. */
86 char *socket2address(int socket
) {
87 struct sockaddr_storage storage
;
88 socklen_t length
= (socklen_t
) sizeof(storage
);
89 char host
[NI_MAXHOST
];
90 char service
[NI_MAXSERV
];
93 if (-1 == getsockname(socket
, (struct sockaddr
*)&storage
, &length
)) {
94 set_server_error("Could not get address of server socket");
98 if (0 != getnameinfo((struct sockaddr
*)&storage
, length
,
99 host
, sizeof(host
), service
, sizeof(service
),
100 NI_NUMERICHOST
|NI_NUMERICSERV
)) {
101 set_server_error("Could not resolve address of server socket");
105 if (-1 == test_asprintf(&address
,
106 (strchr(host
, ':') == NULL
) ? "%s:%s" : "[%s]:%s",
108 set_server_error("Could not format server address");
116 /* Process ISDS WS ping */
117 static void do_ws(int client_socket
,
118 const struct service_configuration
*ws_configuration
,
119 const struct http_request
*request
, const char *required_base_path
) {
120 char *end_point
= request
->uri
; /* Pointer to string in request */
122 if (request
->method
!= HTTP_METHOD_POST
) {
123 http_send_response_400(client_socket
,
124 "Regular ISDS web service request must be POST");
128 if (required_base_path
!= NULL
) {
129 size_t required_base_path_length
= strlen(required_base_path
);
130 if (strncmp(end_point
, required_base_path
, required_base_path_length
)) {
131 http_send_response_400(client_socket
,
132 "Request sent to invalid path");
135 end_point
+= required_base_path_length
;
138 soap(client_socket
, ws_configuration
, request
->body
, request
->body_length
,
143 /* Do the server protocol.
144 * @server_socket is listening TCP socket of the server
145 * @server_arguments is pointer to structure:
146 * Never returns. Terminates by exit(). */
147 void server_basic_authentication(int server_socket
,
148 const void *server_arguments
) {
150 const struct arguments_basic_authentication
*arguments
=
151 (const struct arguments_basic_authentication
*) server_arguments
;
152 struct http_request
*request
= NULL
;
155 if (arguments
== NULL
) {
156 close(server_socket
);
160 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
161 fprintf(stderr
, "Connection accepted\n");
162 error
= http_read_request(client_socket
, &request
);
164 fprintf(stderr
, "Error while reading request\n");
165 if (error
== HTTP_ERROR_CLIENT
)
166 http_send_response_400(client_socket
, "Error in request");
168 http_send_response_500(client_socket
,
169 "Error while reading request");
170 close(client_socket
);
174 if (request
->method
== HTTP_METHOD_POST
) {
175 /* Only POST requests are used in Basic authentication mode */
176 if (arguments
->username
!= NULL
) {
177 if (http_client_authenticates(request
)) {
178 switch(http_authenticate_basic(request
,
179 arguments
->username
, arguments
->password
)) {
180 case HTTP_ERROR_SUCCESS
:
181 do_ws(client_socket
, arguments
->services
, request
,
184 case HTTP_ERROR_CLIENT
:
185 if (arguments
->isds_deviations
)
186 http_send_response_401_basic(client_socket
);
188 http_send_response_403(client_socket
);
191 http_send_response_500(client_socket
,
192 "Server error while verifying Basic "
196 http_send_response_401_basic(client_socket
);
199 do_ws(client_socket
, arguments
->services
, request
,
203 /* HTTP method unsupported per ISDS specification */
204 http_send_response_400(client_socket
,
205 "Only POST method is allowed");
207 http_request_free(&request
);
210 close(client_socket
);
213 close(server_socket
);
218 /* Process first phase of TOTP request */
219 static void do_as_sendsms(int client_socket
, const struct http_request
*request
,
220 const struct arguments_otp_authentication
*arguments
) {
221 if (arguments
== NULL
) {
222 http_send_response_500(client_socket
,
223 "Third argument of do_as_sendsms() is NULL");
227 if (request
->method
!= HTTP_METHOD_POST
) {
228 http_send_response_400(client_socket
,
229 "First phase TOTP request must be POST");
233 if (!http_client_authenticates(request
)) {
234 http_send_response_401_otp(client_socket
,
236 "authentication.error.userIsNotAuthenticated",
237 "Client did not send any authentication header");
241 switch(http_authenticate_basic(request
,
242 arguments
->username
, arguments
->password
)) {
243 case HTTP_ERROR_SUCCESS
: {
245 char *uri
= strstr(request
->uri
, "&uri=");
247 http_send_response_400(client_socket
,
248 "Missing uri parameter in Request URI");
252 /* Build new location for second OTP phase */
253 char *location
= NULL
;
254 if (-1 == test_asprintf(&location
, "%s%s", as_path_dontsendsms
, uri
)) {
255 http_send_response_500(client_socket
,
256 "Could not build new localtion for "
260 char *terminator
= strchr(uri
, '&');
261 if (NULL
!= terminator
)
262 location
[strlen(as_path_dontsendsms
) + (uri
- terminator
)] = '\0';
263 http_send_response_302_totp(client_socket
,
264 "authentication.info.totpSended",
265 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
270 case HTTP_ERROR_CLIENT
:
271 if (arguments
->isds_deviations
)
272 http_send_response_401_otp(client_socket
,
274 "authentication.error.userIsNotAuthenticated",
275 " Retry: Bad user name or password in first OTP phase.\r\n"
276 " This is very long header\r\n"
277 " which should span to more lines.\r\n"
278 " Surrounding LWS are meaning-less. ");
280 http_send_response_403(client_socket
);
283 http_send_response_500(client_socket
,
284 "Could not verify Basic authentication");
289 /* Return static string representation of HTTP OTP authentication method.
290 * Or NULL in case of error. */
291 static const char *auth_otp_method2string(enum auth_otp_method method
) {
293 case AUTH_OTP_HMAC
: return "hotp";
294 case AUTH_OTP_TIME
: return "totp";
295 default: return NULL
;
300 /* Process second phase of OTP request */
301 static void do_as_phase_two(int client_socket
, const struct http_request
*request
,
302 const struct arguments_otp_authentication
*arguments
) {
303 if (arguments
== NULL
) {
304 http_send_response_500(client_socket
,
305 "Third argument of do_as_phase_two() is NULL");
309 if (request
->method
!= HTTP_METHOD_POST
) {
310 http_send_response_400(client_socket
,
311 "Second phase OTP request must be POST");
315 if (!http_client_authenticates(request
)) {
316 http_send_response_401_otp(client_socket
,
317 auth_otp_method2string(arguments
->method
),
318 "authentication.error.userIsNotAuthenticated",
319 "Client did not send any authentication header");
323 switch(http_authenticate_otp(request
,
324 arguments
->username
, arguments
->password
, arguments
->otp
)) {
325 case HTTP_ERROR_SUCCESS
: {
327 char *uri
= strstr(request
->uri
, "&uri=");
329 http_send_response_400(client_socket
,
330 "Missing uri parameter in Request URI");
334 /* Build new location for final request */
335 char *location
= NULL
;
336 if (NULL
== (location
= strdup(uri
))) {
337 http_send_response_500(client_socket
,
338 "Could not build new location for final request");
341 char *terminator
= strchr(location
, '&');
342 if (NULL
!= terminator
)
344 /* Generate pseudo-random cookie value. This is to prevent
345 * client from reusing the cookie accidentally. We use the
346 * same seed to get reproducible tests. */
347 if (-1 == test_asprintf(&authorization_cookie_value
, "%d",
349 http_send_response_500(client_socket
,
350 "Could not generate cookie value");
354 /* XXX: Add Path parameter to cookie, otherwise
355 * different paths will not match.
356 * FIXME: Domain argument does not work with cURL. Report a bug. */
357 http_send_response_302_cookie(client_socket
,
358 authorization_cookie_name
,
359 authorization_cookie_value
,
360 /*http_find_host(request)*/NULL
,
366 case HTTP_ERROR_CLIENT
:
367 if (arguments
->isds_deviations
)
368 http_send_response_401_otp(client_socket
,
369 auth_otp_method2string(arguments
->method
),
370 "authentication.error.userIsNotAuthenticated",
371 " Retry: Bad user name or password in second OTP phase.\r\n"
372 " This is very long header\r\n"
373 " which should span to more lines.\r\n"
374 " Surrounding LWS are meaning-less. ");
376 http_send_response_403(client_socket
);
379 http_send_response_500(client_socket
,
380 "Could not verify OTP authentication");
385 /* Process OTP session cookie invalidation request */
386 static void do_as_logout(int client_socket
, const struct http_request
*request
,
387 const struct arguments_otp_authentication
*arguments
) {
388 if (arguments
== NULL
) {
389 http_send_response_500(client_socket
,
390 "Third argument of do_as_logout() is NULL");
394 if (request
->method
!= HTTP_METHOD_GET
) {
395 http_send_response_400(client_socket
,
396 "OTP cookie invalidation request must be GET");
400 const char *received_cookie
=
401 http_find_cookie(request
, authorization_cookie_name
);
403 if (authorization_cookie_value
== NULL
|| received_cookie
== NULL
||
404 strcmp(authorization_cookie_value
, received_cookie
)) {
405 http_send_response_403(client_socket
);
409 /* XXX: Add Path parameter to cookie, otherwise
410 * different paths will not match.
411 * FIXME: Domain argument does not work with cURL. Report a bug. */
412 http_send_response_200_cookie(client_socket
,
413 authorization_cookie_name
,
415 /*http_find_host(request)*/ NULL
,
421 /* Process ISDS WS ping authorized by cookie */
422 static void do_ws_with_cookie(int client_socket
,
423 const struct http_request
*request
,
424 const struct arguments_otp_authentication
*arguments
,
425 const char *valid_base_path
) {
426 const char *received_cookie
=
427 http_find_cookie(request
, authorization_cookie_name
);
429 if (authorization_cookie_value
!= NULL
&& received_cookie
!= NULL
&&
430 !strcmp(authorization_cookie_value
, received_cookie
))
431 do_ws(client_socket
, arguments
->services
, request
, valid_base_path
);
433 http_send_response_403(client_socket
);
437 /* Do the server protocol with OTP authentication.
438 * @server_socket is listening TCP socket of the server
439 * @server_arguments is pointer to structure arguments_otp_authentication. It
440 * selects OTP method to enable.
441 * Never returns. Terminates by exit(). */
442 void server_otp_authentication(int server_socket
,
443 const void *server_arguments
) {
445 const struct arguments_otp_authentication
*arguments
=
446 (const struct arguments_otp_authentication
*) server_arguments
;
447 struct http_request
*request
= NULL
;
450 if (arguments
== NULL
) {
451 close(server_socket
);
455 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
456 fprintf(stderr
, "Connection accepted\n");
457 error
= http_read_request(client_socket
, &request
);
459 fprintf(stderr
, "Error while reading request\n");
460 if (error
== HTTP_ERROR_CLIENT
)
461 http_send_response_400(client_socket
, "Error in request");
463 http_send_response_500(client_socket
, "Could not read request");
464 close(client_socket
);
468 if (arguments
->username
!= NULL
) {
469 if (arguments
->method
== AUTH_OTP_HMAC
&&
470 !strncmp(request
->uri
, as_path_hotp
, strlen(as_path_hotp
))) {
471 do_as_phase_two(client_socket
, request
, arguments
);
472 } else if (arguments
->method
== AUTH_OTP_TIME
&&
473 !strncmp(request
->uri
, as_path_sendsms
,
474 strlen(as_path_sendsms
))) {
475 do_as_sendsms(client_socket
, request
, arguments
);
476 } else if (arguments
->method
== AUTH_OTP_TIME
&&
477 !strncmp(request
->uri
, as_path_dontsendsms
,
478 strlen(as_path_dontsendsms
))) {
479 do_as_phase_two(client_socket
, request
, arguments
);
480 } else if (!strncmp(request
->uri
, as_path_logout
,
481 strlen(as_path_logout
))) {
482 do_as_logout(client_socket
, request
, arguments
);
483 } else if (!strcmp(request
->uri
, ws_path
)) {
484 do_ws_with_cookie(client_socket
, request
, arguments
,
487 http_send_response_400(client_socket
,
488 "Unknown path for TOTP authenticating service");
491 if (!strcmp(request
->uri
, ws_path
)) {
492 do_ws(client_socket
, arguments
->services
, request
,
495 http_send_response_400(client_socket
,
496 "Unknown path for TOTP authenticating service");
499 http_request_free(&request
);
502 close(client_socket
);
505 close(server_socket
);
506 free(authorization_cookie_value
);
511 /* Implementation of server that is out of order.
512 * It always sends back SOAP Fault with HTTP error 503.
513 * @server_socket is listening TCP socket of the server
514 * @server_arguments is ununsed pointer
515 * Never returns. Terminates by exit(). */
516 void server_out_of_order(int server_socket
,
517 const void *server_arguments
) {
519 struct http_request
*request
= NULL
;
521 const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
522 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>";
524 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
525 fprintf(stderr
, "Connection accepted\n");
526 error
= http_read_request(client_socket
, &request
);
528 fprintf(stderr
, "Error while reading request\n");
529 if (error
== HTTP_ERROR_CLIENT
)
530 http_send_response_400(client_socket
, "Error in request");
531 close(client_socket
);
535 http_send_response_503(client_socket
, fault
, strlen(fault
),
537 http_request_free(&request
);
539 close(client_socket
);
542 close(server_socket
);
547 /* Start sever in separate process.
548 * @server_process is PID of forked server
549 * @server_address is automatically allocated TCP address of listening server
550 * @username sets required user name server has to require. Set NULL to
551 * disable HTTP authentication.
552 * @password sets required password server has to require
554 * @isds_deviations is flag to set conformance level. If false, server is
555 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
556 * specification. Otherwise server mimics real ISDS implementation as much
558 * @return -1 in case of error. */
559 int start_server(pid_t
*server_process
, char **server_address
,
560 void (*server_implementation
)(int, const void *),
561 const void *server_arguments
) {
564 if (server_address
== NULL
) {
565 set_server_error("start_server(): Got invalid server_address pointer");
568 *server_address
= NULL
;
570 if (server_process
== NULL
) {
571 set_server_error("start_server(): Got invalid server_process pointer");
575 server_socket
= listen_on_socket();
576 if (server_socket
== -1) {
577 set_server_error("Could not create listening socket");
581 *server_address
= socket2address(server_socket
);
582 if (*server_address
== NULL
) {
583 close(server_socket
);
584 set_server_error("Could not format address of listening address");
588 *server_process
= fork();
589 if (*server_process
== -1) {
590 close(server_socket
);
591 set_server_error("Server could not been forked");
595 if (*server_process
== 0) {
596 server_implementation(server_socket
, server_arguments
);
597 /* Does not return */
604 /* Kill the server process.
605 * Return -1 in case of error. */
606 int stop_server(pid_t server_process
) {
607 if (server_process
<= 0) {
608 set_server_error("Invalid server PID to kill");
611 if (-1 == kill(server_process
, SIGTERM
)) {
612 set_server_error("Could not terminate server");
615 if (-1 == waitpid(server_process
, NULL
, 0)) {
616 set_server_error("Could not wait for server termination");