0.6.1 bump
[libisds.git] / test / simline / server.c
blob1c008eb0f6df496db242da4454fbcf03d8aebfe5
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 <netdb.h>
19 #include <string.h>
20 #include <signal.h>
21 #include <unistd.h>
22 #include <wait.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 *asws_path = "/asws/changePassword";
31 static const char *ws_path = "/apps/DS/dz";
33 static const char *ws_base_path_basic = "/";
34 static const char *ws_base_path_otp = "/apps/";
36 static const char *authorization_cookie_name = "IPCZ-X-COOKIE";
37 static char *authorization_cookie_value = NULL;
39 /* Save pointer to static error message if not yet set */
40 void set_server_error(const char *message) {
41 if (server_error == NULL) {
42 server_error = message;
47 /* Creates listening TCP socket on localhost.
48 * Returns the socket descriptor or -1. */
49 int listen_on_socket(void) {
50 int retval;
51 struct addrinfo hints;
52 struct addrinfo *addresses, *address;
53 int fd;
55 memset(&hints, 0, sizeof(hints));
56 hints.ai_family = AF_UNSPEC;
57 hints.ai_socktype = SOCK_STREAM;
58 retval = getaddrinfo("localhost", NULL, &hints, &addresses);
59 if (retval) {
60 set_server_error("Could not resolve `localhost'");
61 return -1;
64 for (address = addresses; address != NULL; address = address->ai_next) {
65 fd = socket(address->ai_family, address->ai_socktype,
66 address->ai_protocol);
67 if (fd == -1) continue;
69 retval = bind(fd, address->ai_addr, address->ai_addrlen);
70 if (retval != 0) continue;
72 retval = listen(fd, 0);
73 if (retval == 0) {
74 freeaddrinfo(addresses);
75 return fd;
79 freeaddrinfo(addresses);
80 set_server_error("Could not start listening on TCP/localhost");
81 return -1;
85 /* Format socket address as printable string.
86 * @return allocated string or NULL in case of error. */
87 char *socket2address(int socket) {
88 struct sockaddr_storage storage;
89 socklen_t length = (socklen_t) sizeof(storage);
90 char host[NI_MAXHOST];
91 char service[NI_MAXSERV];
92 char *address = NULL;
94 if (-1 == getsockname(socket, (struct sockaddr *)&storage, &length)) {
95 set_server_error("Could not get address of server socket");
96 return NULL;
99 if (0 != getnameinfo((struct sockaddr *)&storage, length,
100 host, sizeof(host), service, sizeof(service),
101 NI_NUMERICHOST|NI_NUMERICSERV)) {
102 set_server_error("Could not resolve address of server socket");
103 return NULL;
106 if (-1 == test_asprintf(&address,
107 (strchr(host, ':') == NULL) ? "%s:%s" : "[%s]:%s",
108 host, service)) {
109 set_server_error("Could not format server address");
110 return NULL;
113 return address;
117 /* Process ISDS web service */
118 static void do_ws(int client_socket,
119 const struct service_configuration *ws_configuration,
120 const struct http_request *request, const char *required_base_path) {
121 char *end_point = request->uri; /* Pointer to string in the request */
123 if (request->method != HTTP_METHOD_POST) {
124 http_send_response_400(client_socket,
125 "Regular ISDS web service request must be POST");
126 return;
129 if (required_base_path != NULL) {
130 size_t required_base_path_length = strlen(required_base_path);
131 if (strncmp(end_point, required_base_path, required_base_path_length)) {
132 http_send_response_400(client_socket,
133 "Request sent to invalid path");
134 return;
136 end_point += required_base_path_length;
139 soap(client_socket, ws_configuration, request->body, request->body_length,
140 end_point);
144 /* Do the server protocol.
145 * @server_socket is listening TCP socket of the server
146 * @server_arguments is pointer to structure:
147 * Never returns. Terminates by exit(). */
148 void server_basic_authentication(int server_socket,
149 const void *server_arguments) {
150 int client_socket;
151 const struct arguments_basic_authentication *arguments =
152 (const struct arguments_basic_authentication *) server_arguments;
153 struct http_request *request = NULL;
154 http_error error;
156 if (arguments == NULL) {
157 close(server_socket);
158 exit(EXIT_FAILURE);
161 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
162 fprintf(stderr, "Connection accepted\n");
163 error = http_read_request(client_socket, &request);
164 if (error) {
165 fprintf(stderr, "Error while reading request\n");
166 if (error == HTTP_ERROR_CLIENT)
167 http_send_response_400(client_socket, "Error in request");
168 else
169 http_send_response_500(client_socket,
170 "Error while reading request");
171 close(client_socket);
172 continue;
175 if (request->method == HTTP_METHOD_POST) {
176 /* Only POST requests are used in Basic authentication mode */
177 if (arguments->username != NULL) {
178 if (http_client_authenticates(request)) {
179 switch(http_authenticate_basic(request,
180 arguments->username, arguments->password)) {
181 case HTTP_ERROR_SUCCESS:
182 do_ws(client_socket, arguments->services, request,
183 ws_base_path_basic);
184 break;
185 case HTTP_ERROR_CLIENT:
186 if (arguments->isds_deviations)
187 http_send_response_401_basic(client_socket);
188 else
189 http_send_response_403(client_socket);
190 break;
191 default:
192 http_send_response_500(client_socket,
193 "Server error while verifying Basic "
194 "authentication");
196 } else {
197 http_send_response_401_basic(client_socket);
199 } else {
200 do_ws(client_socket, arguments->services, request,
201 ws_base_path_basic);
203 } else {
204 /* HTTP method unsupported per ISDS specification */
205 http_send_response_400(client_socket,
206 "Only POST method is allowed");
208 http_request_free(&request);
211 close(client_socket);
214 close(server_socket);
215 exit(EXIT_SUCCESS);
219 /* Process first phase of TOTP request */
220 static void do_as_sendsms(int client_socket, const struct http_request *request,
221 const struct arguments_otp_authentication *arguments) {
222 if (arguments == NULL) {
223 http_send_response_500(client_socket,
224 "Third argument of do_as_sendsms() is NULL");
225 return;
228 if (request->method != HTTP_METHOD_POST) {
229 http_send_response_400(client_socket,
230 "First phase TOTP request must be POST");
231 return;
234 if (!http_client_authenticates(request)) {
235 http_send_response_401_otp(client_socket,
236 "totpsendsms",
237 "authentication.error.userIsNotAuthenticated",
238 "Client did not send any authentication header");
239 return;
242 switch(http_authenticate_basic(request,
243 arguments->username, arguments->password)) {
244 case HTTP_ERROR_SUCCESS: {
245 /* Find final URI */
246 char *uri = strstr(request->uri, "&uri=");
247 if (uri == NULL) {
248 http_send_response_400(client_socket,
249 "Missing uri parameter in Request URI");
250 return;
252 uri += 5;
253 /* Build new location for second OTP phase */
254 char *location = NULL;
255 if (-1 == test_asprintf(&location, "%s%s", as_path_dontsendsms, uri)) {
256 http_send_response_500(client_socket,
257 "Could not build new localtion for "
258 "second OTP phase");
259 return;
261 char *terminator = strchr(uri, '&');
262 if (NULL != terminator)
263 location[strlen(as_path_dontsendsms) + (uri - terminator)] = '\0';
264 http_send_response_302_totp(client_socket,
265 "authentication.info.totpSended",
266 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
267 location);
268 free(location);
270 break;
271 case HTTP_ERROR_CLIENT:
272 if (arguments->isds_deviations)
273 http_send_response_401_otp(client_socket,
274 "totpsendsms",
275 "authentication.error.userIsNotAuthenticated",
276 " Retry: Bad user name or password in first OTP phase.\r\n"
277 " This is very long header\r\n"
278 " which should span to more lines.\r\n"
279 " Surrounding LWS are meaning-less. ");
280 else
281 http_send_response_403(client_socket);
282 break;
283 default:
284 http_send_response_500(client_socket,
285 "Could not verify Basic authentication");
290 /* Return static string representation of HTTP OTP authentication method.
291 * Or NULL in case of error. */
292 static const char *auth_otp_method2string(enum auth_otp_method method) {
293 switch (method) {
294 case AUTH_OTP_HMAC: return "hotp";
295 case AUTH_OTP_TIME: return "totp";
296 default: return NULL;
301 /* Process second phase of OTP request */
302 static void do_as_phase_two(int client_socket, const struct http_request *request,
303 const struct arguments_otp_authentication *arguments) {
304 if (arguments == NULL) {
305 http_send_response_500(client_socket,
306 "Third argument of do_as_phase_two() is NULL");
307 return;
310 if (request->method != HTTP_METHOD_POST) {
311 http_send_response_400(client_socket,
312 "Second phase OTP request must be POST");
313 return;
316 if (!http_client_authenticates(request)) {
317 http_send_response_401_otp(client_socket,
318 auth_otp_method2string(arguments->method),
319 "authentication.error.userIsNotAuthenticated",
320 "Client did not send any authentication header");
321 return;
324 switch(http_authenticate_otp(request,
325 arguments->username, arguments->password, arguments->otp)) {
326 case HTTP_ERROR_SUCCESS: {
327 /* Find final URI */
328 char *uri = strstr(request->uri, "&uri=");
329 if (uri == NULL) {
330 http_send_response_400(client_socket,
331 "Missing uri parameter in Request URI");
332 return;
334 uri += 5;
335 /* Build new location for final request */
336 char *location = NULL;
337 if (NULL == (location = strdup(uri))) {
338 http_send_response_500(client_socket,
339 "Could not build new location for final request");
340 return;
342 char *terminator = strchr(location, '&');
343 if (NULL != terminator)
344 *terminator = '\0';
345 /* Generate pseudo-random cookie value. This is to prevent
346 * client from reusing the cookie accidentally. We use the
347 * same seed to get reproducible tests. */
348 if (-1 == test_asprintf(&authorization_cookie_value, "%d",
349 rand())) {
350 http_send_response_500(client_socket,
351 "Could not generate cookie value");
352 free(location);
353 return;
355 /* XXX: Add Path parameter to cookie, otherwise
356 * different paths will not match.
357 * FIXME: Domain argument does not work with cURL. Report a bug. */
358 http_send_response_302_cookie(client_socket,
359 authorization_cookie_name,
360 authorization_cookie_value,
361 /*http_find_host(request)*/NULL,
362 /*NULL*/"/",
363 location);
364 free(location);
366 break;
367 case HTTP_ERROR_CLIENT:
368 if (arguments->isds_deviations)
369 http_send_response_401_otp(client_socket,
370 auth_otp_method2string(arguments->method),
371 "authentication.error.userIsNotAuthenticated",
372 " Retry: Bad user name or password in second OTP phase.\r\n"
373 " This is very long header\r\n"
374 " which should span to more lines.\r\n"
375 " Surrounding LWS are meaning-less. ");
376 else
377 http_send_response_403(client_socket);
378 break;
379 default:
380 http_send_response_500(client_socket,
381 "Could not verify OTP authentication");
386 /* Process ASWS for changing OTP password requests */
387 /* FIXME: The ASWS URI hosts two services: for sending TOTP code for password
388 * change and for changing OTP password. The problem is the former one is
389 * basic-authenticated, the later one is otp-auhenticated. But we cannot decide
390 * which authentication to enforce without understadning request body.
391 * I will just try both of them to choose the service.
392 * But I hope official server implementation does it in more clever way. */
393 static void do_asws(int client_socket, const struct http_request *request,
394 const struct arguments_otp_authentication *arguments) {
395 http_error error;
396 if (arguments == NULL) {
397 http_send_response_500(client_socket,
398 "Third argument of do_asws() is NULL");
399 return;
402 if (request->method != HTTP_METHOD_POST) {
403 http_send_response_400(client_socket,
404 "ASWS request must be POST");
405 return;
408 if (!http_client_authenticates(request)) {
409 http_send_response_401_otp(client_socket,
410 auth_otp_method2string(arguments->method),
411 "authentication.error.userIsNotAuthenticated",
412 "Client did not send any authentication header");
413 return;
416 /* Try OTP */
417 error = http_authenticate_otp(request,
418 arguments->username, arguments->password, arguments->otp);
419 if (HTTP_ERROR_SUCCESS == error) {
420 /* This will be request for password change because OTP
421 * authentication succeeded. */
422 do_ws(client_socket, arguments->services, request, NULL);
423 return;
426 if (AUTH_OTP_TIME == arguments->method) {
427 /* Try Basic */
428 error = http_authenticate_basic(request,
429 arguments->username, arguments->password);
430 if (HTTP_ERROR_SUCCESS == error) {
431 /* This will be request for time code for password change because
432 * basic authentication succeeded. */
433 do_ws(client_socket, arguments->services, request, NULL);
434 return;
438 if (HTTP_ERROR_CLIENT == error) {
439 if (arguments->isds_deviations)
440 http_send_response_401_otp(client_socket,
441 auth_otp_method2string(arguments->method),
442 "authentication.error.userIsNotAuthenticated",
443 " Retry: Bad user name or password in Authorization.\r\n"
444 " This is very long header\r\n"
445 " which should span to more lines.\r\n"
446 " Surrounding LWS are meaning-less. ");
447 else
448 http_send_response_403(client_socket);
449 return;
452 http_send_response_500(client_socket,
453 "Could not verify OTP authentication");
457 /* Process OTP session cookie invalidation request */
458 static void do_as_logout(int client_socket, const struct http_request *request,
459 const struct arguments_otp_authentication *arguments) {
460 if (arguments == NULL) {
461 http_send_response_500(client_socket,
462 "Third argument of do_as_logout() is NULL");
463 return;
466 if (request->method != HTTP_METHOD_GET) {
467 http_send_response_400(client_socket,
468 "OTP cookie invalidation request must be GET");
469 return;
472 const char *received_cookie =
473 http_find_cookie(request, authorization_cookie_name);
475 if (authorization_cookie_value == NULL || received_cookie == NULL ||
476 strcmp(authorization_cookie_value, received_cookie)) {
477 http_send_response_403(client_socket);
478 return;
481 /* XXX: Add Path parameter to cookie, otherwise
482 * different paths will not match.
483 * FIXME: Domain argument does not work with cURL. Report a bug. */
484 http_send_response_200_cookie(client_socket,
485 authorization_cookie_name,
487 /*http_find_host(request)*/ NULL,
488 /*NULL*/"/",
489 NULL, 0, NULL);
493 /* Process ISDS WS ping authorized by cookie */
494 static void do_ws_with_cookie(int client_socket,
495 const struct http_request *request,
496 const struct arguments_otp_authentication *arguments,
497 const char *valid_base_path) {
498 const char *received_cookie =
499 http_find_cookie(request, authorization_cookie_name);
501 if (authorization_cookie_value != NULL && received_cookie != NULL &&
502 !strcmp(authorization_cookie_value, received_cookie))
503 do_ws(client_socket, arguments->services, request, valid_base_path);
504 else
505 http_send_response_403(client_socket);
509 /* Do the server protocol with OTP authentication.
510 * @server_socket is listening TCP socket of the server
511 * @server_arguments is pointer to structure arguments_otp_authentication. It
512 * selects OTP method to enable.
513 * Never returns. Terminates by exit(). */
514 void server_otp_authentication(int server_socket,
515 const void *server_arguments) {
516 int client_socket;
517 const struct arguments_otp_authentication *arguments =
518 (const struct arguments_otp_authentication *) server_arguments;
519 struct http_request *request = NULL;
520 http_error error;
522 if (arguments == NULL) {
523 close(server_socket);
524 exit(EXIT_FAILURE);
527 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
528 fprintf(stderr, "Connection accepted\n");
529 error = http_read_request(client_socket, &request);
530 if (error) {
531 fprintf(stderr, "Error while reading request\n");
532 if (error == HTTP_ERROR_CLIENT)
533 http_send_response_400(client_socket, "Error in request");
534 else
535 http_send_response_500(client_socket, "Could not read request");
536 close(client_socket);
537 continue;
540 if (arguments->username != NULL) {
541 if (arguments->method == AUTH_OTP_HMAC &&
542 !strncmp(request->uri, as_path_hotp, strlen(as_path_hotp))) {
543 do_as_phase_two(client_socket, request, arguments);
544 } else if (arguments->method == AUTH_OTP_TIME &&
545 !strncmp(request->uri, as_path_sendsms,
546 strlen(as_path_sendsms))) {
547 do_as_sendsms(client_socket, request, arguments);
548 } else if (arguments->method == AUTH_OTP_TIME &&
549 !strncmp(request->uri, as_path_dontsendsms,
550 strlen(as_path_dontsendsms))) {
551 do_as_phase_two(client_socket, request, arguments);
552 } else if (!strncmp(request->uri, as_path_logout,
553 strlen(as_path_logout))) {
554 do_as_logout(client_socket, request, arguments);
555 } else if (!strcmp(request->uri, asws_path)) {
556 do_asws(client_socket, request, arguments);
557 } else if (!strcmp(request->uri, ws_path)) {
558 do_ws_with_cookie(client_socket, request, arguments,
559 ws_base_path_otp);
560 } else {
561 http_send_response_400(client_socket,
562 "Unknown path for OTP authenticating service");
564 } else {
565 if (!strcmp(request->uri, ws_path)) {
566 do_ws(client_socket, arguments->services, request,
567 ws_base_path_otp);
568 } else {
569 http_send_response_400(client_socket,
570 "Unknown path for OTP authenticating service");
573 http_request_free(&request);
576 close(client_socket);
579 close(server_socket);
580 free(authorization_cookie_value);
581 exit(EXIT_SUCCESS);
585 /* Implementation of server that is out of order.
586 * It always sends back SOAP Fault with HTTP error 503.
587 * @server_socket is listening TCP socket of the server
588 * @server_arguments is ununsed pointer
589 * Never returns. Terminates by exit(). */
590 void server_out_of_order(int server_socket,
591 const void *server_arguments) {
592 int client_socket;
593 struct http_request *request = NULL;
594 http_error error;
595 const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
596 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>";
598 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
599 fprintf(stderr, "Connection accepted\n");
600 error = http_read_request(client_socket, &request);
601 if (error) {
602 fprintf(stderr, "Error while reading request\n");
603 if (error == HTTP_ERROR_CLIENT)
604 http_send_response_400(client_socket, "Error in request");
605 close(client_socket);
606 continue;
609 http_send_response_503(client_socket, fault, strlen(fault),
610 soap_mime_type);
611 http_request_free(&request);
613 close(client_socket);
616 close(server_socket);
617 exit(EXIT_SUCCESS);
621 /* Start sever in separate process.
622 * @server_process is PID of forked server
623 * @server_address is automatically allocated TCP address of listening server
624 * @username sets required user name server has to require. Set NULL to
625 * disable HTTP authentication.
626 * @password sets required password server has to require
627 * socket.
628 * @isds_deviations is flag to set conformance level. If false, server is
629 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
630 * specification. Otherwise server mimics real ISDS implementation as much
631 * as possible.
632 * @return -1 in case of error. */
633 int start_server(pid_t *server_process, char **server_address,
634 void (*server_implementation)(int, const void *),
635 const void *server_arguments) {
636 int server_socket;
638 if (server_address == NULL) {
639 set_server_error("start_server(): Got invalid server_address pointer");
640 return -1;
642 *server_address = NULL;
644 if (server_process == NULL) {
645 set_server_error("start_server(): Got invalid server_process pointer");
646 return -1;
649 server_socket = listen_on_socket();
650 if (server_socket == -1) {
651 set_server_error("Could not create listening socket");
652 return -1;
655 *server_address = socket2address(server_socket);
656 if (*server_address == NULL) {
657 close(server_socket);
658 set_server_error("Could not format address of listening address");
659 return -1;
662 *server_process = fork();
663 if (*server_process == -1) {
664 close(server_socket);
665 set_server_error("Server could not been forked");
666 return -1;
669 if (*server_process == 0) {
670 server_implementation(server_socket, server_arguments);
671 /* Does not return */
674 return 0;
678 /* Kill the server process.
679 * Return -1 in case of error. */
680 int stop_server(pid_t server_process) {
681 if (server_process <= 0) {
682 set_server_error("Invalid server PID to kill");
683 return -1;
685 if (-1 == kill(server_process, SIGTERM)) {
686 set_server_error("Could not terminate server");
687 return -1;
689 if (-1 == waitpid(server_process, NULL, 0)) {
690 set_server_error("Could not wait for server termination");
691 return -1;
693 return 0;