2 #define _POSIX_SOURCE /* For getaddrinfo(3) */
6 #define _BSD_SOURCE /* For NI_MAXHOST */
9 #include "../test-tools.h"
15 #include <sys/types.h>
16 #include <sys/socket.h>
23 const char *server_error
= NULL
;
25 static const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
26 /* DummyOperation respons */
27 static const char *pong
= "<?xml version='1.0' encoding='utf-8'?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><SOAP-ENV:Body><q:DummyOperationResponse xmlns:q=\"http://isds.czechpoint.cz/v20\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><q:dmStatus><q:dmStatusCode>0000</q:dmStatusCode><q:dmStatusMessage>Provedeno úspěšně.</q:dmStatusMessage></q:dmStatus></q:DummyOperationResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>";
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 *ws_path
= "/apps/DS/dz";
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
, const struct http_request
*request
) {
118 if (request
->method
!= HTTP_METHOD_POST
) {
119 http_send_response_400(client_socket
,
120 "Regular ISDS web service request must be POST");
124 http_send_response_200(client_socket
, pong
, strlen(pong
), soap_mime_type
);
128 /* Do the server protocol.
129 * @server_socket is listening TCP socket of the server
130 * @server_arguments is pointer to structure:
131 * Never returns. Terminates by exit(). */
132 void server_basic_authentication(int server_socket
,
133 const void *server_arguments
) {
135 const struct arguments_basic_authentication
*arguments
=
136 (const struct arguments_basic_authentication
*) server_arguments
;
137 struct http_request
*request
= NULL
;
140 if (arguments
== NULL
) {
141 close(server_socket
);
145 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
146 fprintf(stderr
, "Connection accepted\n");
147 error
= http_read_request(client_socket
, &request
);
149 fprintf(stderr
, "Error while reading request\n");
150 if (error
== HTTP_ERROR_CLIENT
)
151 http_send_response_400(client_socket
, "Error in request");
153 http_send_response_500(client_socket
);
154 close(client_socket
);
158 if (request
->method
== HTTP_METHOD_POST
) {
159 /* Only POST requests are used in Basic authentication mode */
160 if (arguments
->username
!= NULL
) {
161 if (http_client_authenticates(request
)) {
162 switch(http_authenticate_basic(request
,
163 arguments
->username
, arguments
->password
)) {
164 case HTTP_ERROR_SUCCESS
:
165 do_ws(client_socket
, request
);
167 case HTTP_ERROR_CLIENT
:
168 if (arguments
->isds_deviations
)
169 http_send_response_401_basic(client_socket
);
171 http_send_response_403(client_socket
);
174 http_send_response_500(client_socket
);
177 http_send_response_401_basic(client_socket
);
180 do_ws(client_socket
, request
);
183 /* HTTP method unsupported per ISDS specification */
184 http_send_response_400(client_socket
,
185 "Only POST method is allowed");
187 http_request_free(&request
);
190 close(client_socket
);
193 close(server_socket
);
198 /* Process first phase of TOTP request */
199 static void do_as_sendsms(int client_socket
, const struct http_request
*request
,
200 const struct arguments_otp_authentication
*arguments
) {
201 if (arguments
== NULL
) {
202 http_send_response_500(client_socket
);
206 if (request
->method
!= HTTP_METHOD_POST
) {
207 http_send_response_400(client_socket
,
208 "First phase TOTP request must be POST");
212 if (!http_client_authenticates(request
)) {
213 http_send_response_401_otp(client_socket
,
215 "authentication.error.userIsNotAuthenticated",
216 "Client did not send any authentication header");
220 switch(http_authenticate_basic(request
,
221 arguments
->username
, arguments
->password
)) {
222 case HTTP_ERROR_SUCCESS
: {
224 char *uri
= strstr(request
->uri
, "&uri=");
226 http_send_response_400(client_socket
,
227 "Missing uri parameter in Request URI");
231 /* Build new location for second OTP phase */
232 char *location
= NULL
;
233 if (-1 == test_asprintf(&location
, "%s%s", as_path_dontsendsms
, uri
)) {
234 http_send_response_500(client_socket
);
237 char *terminator
= strchr(uri
, '&');
238 if (NULL
!= terminator
)
239 location
[strlen(as_path_dontsendsms
) + (uri
- terminator
)] = '\0';
240 http_send_response_302_totp(client_socket
,
241 "authentication.info.totpSended",
242 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
247 case HTTP_ERROR_CLIENT
:
248 if (arguments
->isds_deviations
)
249 http_send_response_401_otp(client_socket
,
251 "authentication.error.userIsNotAuthenticated",
252 " Retry: Bad user name or password in first OTP phase.\r\n"
253 " This is very long header\r\n"
254 " which should span to more lines.\r\n"
255 " Surrounding LWS are meaning-less. ");
257 http_send_response_403(client_socket
);
260 http_send_response_500(client_socket
);
265 /* Return static string representation of HTTP OTP authentication method.
266 * Or NULL in case of error. */
267 static const char *auth_otp_method2string(enum auth_otp_method method
) {
269 case AUTH_OTP_HMAC
: return "hotp";
270 case AUTH_OTP_TIME
: return "totp";
271 default: return NULL
;
276 /* Process second phase of OTP request */
277 static void do_as_phase_two(int client_socket
, const struct http_request
*request
,
278 const struct arguments_otp_authentication
*arguments
) {
279 if (arguments
== NULL
) {
280 http_send_response_500(client_socket
);
284 if (request
->method
!= HTTP_METHOD_POST
) {
285 http_send_response_400(client_socket
,
286 "Second phase OTP request must be POST");
290 if (!http_client_authenticates(request
)) {
291 http_send_response_401_otp(client_socket
,
292 auth_otp_method2string(arguments
->method
),
293 "authentication.error.userIsNotAuthenticated",
294 "Client did not send any authentication header");
298 switch(http_authenticate_otp(request
,
299 arguments
->username
, arguments
->password
, arguments
->otp
)) {
300 case HTTP_ERROR_SUCCESS
: {
302 char *uri
= strstr(request
->uri
, "&uri=");
304 http_send_response_400(client_socket
,
305 "Missing uri parameter in Request URI");
309 /* Build new location for final request */
310 char *location
= NULL
;
311 if (NULL
== (location
= strdup(uri
))) {
312 http_send_response_500(client_socket
);
315 char *terminator
= strchr(location
, '&');
316 if (NULL
!= terminator
)
318 /* Generate pseudo-random cookie value. This is to prevent
319 * client from reusing the cookie accidentally. We use the
320 * same seed to get reproducible tests. */
321 if (-1 == test_asprintf(&authorization_cookie_value
, "%d",
323 http_send_response_500(client_socket
);
327 /* XXX: Add Path parameter to cookie, otherwise
328 * different paths will not match.
329 * FIXME: Domain argument does not work with cURL. Report a bug. */
330 http_send_response_302_cookie(client_socket
,
331 authorization_cookie_name
,
332 authorization_cookie_value
,
333 /*http_find_host(request)*/NULL
,
339 case HTTP_ERROR_CLIENT
:
340 if (arguments
->isds_deviations
)
341 http_send_response_401_otp(client_socket
,
342 auth_otp_method2string(arguments
->method
),
343 "authentication.error.userIsNotAuthenticated",
344 " Retry: Bad user name or password in second OTP phase.\r\n"
345 " This is very long header\r\n"
346 " which should span to more lines.\r\n"
347 " Surrounding LWS are meaning-less. ");
349 http_send_response_403(client_socket
);
352 http_send_response_500(client_socket
);
357 /* Process OTP session cookie invalidation request */
358 static void do_as_logout(int client_socket
, const struct http_request
*request
,
359 const struct arguments_otp_authentication
*arguments
) {
360 if (arguments
== NULL
) {
361 http_send_response_500(client_socket
);
365 if (request
->method
!= HTTP_METHOD_GET
) {
366 http_send_response_400(client_socket
,
367 "OTP cookie invalidation request must be GET");
371 const char *received_cookie
=
372 http_find_cookie(request
, authorization_cookie_name
);
374 if (authorization_cookie_value
== NULL
|| received_cookie
== NULL
||
375 strcmp(authorization_cookie_value
, received_cookie
)) {
376 http_send_response_403(client_socket
);
380 /* XXX: Add Path parameter to cookie, otherwise
381 * different paths will not match.
382 * FIXME: Domain argument does not work with cURL. Report a bug. */
383 http_send_response_200_cookie(client_socket
,
384 authorization_cookie_name
,
386 /*http_find_host(request)*/ NULL
,
392 /* Process ISDS WS ping authorized by cookie */
393 static void do_ws_with_cookie(int client_socket
, const struct http_request
*request
,
394 const struct arguments_otp_authentication
*arguments
) {
395 const char *received_cookie
=
396 http_find_cookie(request
, authorization_cookie_name
);
398 if (authorization_cookie_value
!= NULL
&& received_cookie
!= NULL
&&
399 !strcmp(authorization_cookie_value
, received_cookie
))
400 do_ws(client_socket
, request
);
402 http_send_response_403(client_socket
);
406 /* Do the server protocol with OTP authentication.
407 * @server_socket is listening TCP socket of the server
408 * @server_arguments is pointer to structure arguments_otp_authentication. It
409 * selects OTP method to enable.
410 * Never returns. Terminates by exit(). */
411 void server_otp_authentication(int server_socket
,
412 const void *server_arguments
) {
414 const struct arguments_otp_authentication
*arguments
=
415 (const struct arguments_otp_authentication
*) server_arguments
;
416 struct http_request
*request
= NULL
;
419 if (arguments
== NULL
) {
420 close(server_socket
);
424 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
425 fprintf(stderr
, "Connection accepted\n");
426 error
= http_read_request(client_socket
, &request
);
428 fprintf(stderr
, "Error while reading request\n");
429 if (error
== HTTP_ERROR_CLIENT
)
430 http_send_response_400(client_socket
, "Error in request");
432 http_send_response_500(client_socket
);
433 close(client_socket
);
437 if (arguments
->username
!= NULL
) {
438 if (arguments
->method
== AUTH_OTP_HMAC
&&
439 !strncmp(request
->uri
, as_path_hotp
, strlen(as_path_hotp
))) {
440 do_as_phase_two(client_socket
, request
, arguments
);
441 } else if (arguments
->method
== AUTH_OTP_TIME
&&
442 !strncmp(request
->uri
, as_path_sendsms
,
443 strlen(as_path_sendsms
))) {
444 do_as_sendsms(client_socket
, request
, arguments
);
445 } else if (arguments
->method
== AUTH_OTP_TIME
&&
446 !strncmp(request
->uri
, as_path_dontsendsms
,
447 strlen(as_path_dontsendsms
))) {
448 do_as_phase_two(client_socket
, request
, arguments
);
449 } else if (!strncmp(request
->uri
, as_path_logout
,
450 strlen(as_path_logout
))) {
451 do_as_logout(client_socket
, request
, arguments
);
452 } else if (!strcmp(request
->uri
, ws_path
)) {
453 do_ws_with_cookie(client_socket
, request
, arguments
);
455 http_send_response_400(client_socket
,
456 "Unknown path for TOTP authenticating service");
459 if (!strcmp(request
->uri
, ws_path
)) {
460 do_ws(client_socket
, request
);
462 http_send_response_400(client_socket
,
463 "Unknown path for TOTP authenticating service");
466 http_request_free(&request
);
469 close(client_socket
);
472 close(server_socket
);
473 free(authorization_cookie_value
);
478 /* Implementation of server that is out of order.
479 * It always sends back SOAP Fault with HTTP error 503.
480 * @server_socket is listening TCP socket of the server
481 * @server_arguments is ununsed pointer
482 * Never returns. Terminates by exit(). */
483 void server_out_of_order(int server_socket
,
484 const void *server_arguments
) {
486 struct http_request
*request
= NULL
;
488 const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
489 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>";
491 while (0 <= (client_socket
= accept(server_socket
, NULL
, NULL
))) {
492 fprintf(stderr
, "Connection accepted\n");
493 error
= http_read_request(client_socket
, &request
);
495 fprintf(stderr
, "Error while reading request\n");
496 if (error
== HTTP_ERROR_CLIENT
)
497 http_send_response_400(client_socket
, "Error in request");
498 close(client_socket
);
502 http_send_response_503(client_socket
, fault
, strlen(fault
),
504 http_request_free(&request
);
506 close(client_socket
);
509 close(server_socket
);
514 /* Start sever in separate process.
515 * @server_process is PID of forked server
516 * @server_address is automatically allocated TCP address of listening server
517 * @username sets required user name server has to require. Set NULL to
518 * disable HTTP authentication.
519 * @password sets required password server has to require
521 * @isds_deviations is flag to set conformance level. If false, server is
522 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
523 * specification. Otherwise server mimics real ISDS implementation as much
525 * @return -1 in case of error. */
526 int start_server(pid_t
*server_process
, char **server_address
,
527 void (*server_implementation
)(int, const void *),
528 const void *server_arguments
) {
531 if (server_address
== NULL
) {
532 set_server_error("start_server(): Got invalid server_address pointer");
535 *server_address
= NULL
;
537 if (server_process
== NULL
) {
538 set_server_error("start_server(): Got invalid server_process pointer");
542 server_socket
= listen_on_socket();
543 if (server_socket
== -1) {
544 set_server_error("Could not create listening socket");
548 *server_address
= socket2address(server_socket
);
549 if (*server_address
== NULL
) {
550 close(server_socket
);
551 set_server_error("Could not format address of listening address");
555 *server_process
= fork();
556 if (*server_process
== -1) {
557 close(server_socket
);
558 set_server_error("Server could not been forked");
562 if (*server_process
== 0) {
563 server_implementation(server_socket
, server_arguments
);
564 /* Does not return */
571 /* Kill the server process.
572 * Return -1 in case of error. */
573 int stop_server(pid_t server_process
) {
574 if (server_process
<= 0) {
575 set_server_error("Invalid server PID to kill");
578 if (-1 == kill(server_process
, SIGTERM
)) {
579 set_server_error("Could not terminate server");
582 if (-1 == waitpid(server_process
, NULL
, 0)) {
583 set_server_error("Could not wait for server termination");