test: Move tls_authentication argument from server implementations to start_server()
[libisds.git] / test / simline / server.c
blob6b38a0e75910b4fe3fd2b7a1442851344710cdd9
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>
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) {
51 int retval;
52 struct addrinfo hints;
53 struct addrinfo *addresses, *address;
54 int fd;
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);
60 if (retval) {
61 set_server_error("Could not resolve `localhost'");
62 return -1;
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);
74 if (retval == 0) {
75 freeaddrinfo(addresses);
76 return fd;
80 freeaddrinfo(addresses);
81 set_server_error("Could not start listening on TCP/localhost");
82 return -1;
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];
93 char *address = NULL;
95 if (-1 == getsockname(socket, (struct sockaddr *)&storage, &length)) {
96 set_server_error("Could not get address of server socket");
97 return NULL;
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");
104 return NULL;
107 if (-1 == test_asprintf(&address,
108 (strchr(host, ':') == NULL) ? "%s:%s" : "[%s]:%s",
109 host, service)) {
110 set_server_error("Could not format server address");
111 return NULL;
114 return 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");
127 return;
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");
135 return;
137 end_point += required_base_path_length;
140 soap(client_socket, ws_configuration, request->body, request->body_length,
141 end_point);
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) {
151 int client_socket;
152 const struct arguments_basic_authentication *arguments =
153 (const struct arguments_basic_authentication *) server_arguments;
154 struct http_request *request = NULL;
155 http_error error;
157 if (arguments == NULL) {
158 close(server_socket);
159 exit(EXIT_FAILURE);
162 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
163 fprintf(stderr, "Connection accepted\n");
164 error = http_read_request(client_socket, &request);
165 if (error) {
166 fprintf(stderr, "Error while reading request\n");
167 if (error == HTTP_ERROR_CLIENT)
168 http_send_response_400(client_socket, "Error in request");
169 else
170 http_send_response_500(client_socket,
171 "Error while reading request");
172 close(client_socket);
173 continue;
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,
184 ws_base_path_basic);
185 break;
186 case HTTP_ERROR_CLIENT:
187 if (arguments->isds_deviations)
188 http_send_response_401_basic(client_socket);
189 else
190 http_send_response_403(client_socket);
191 break;
192 default:
193 http_send_response_500(client_socket,
194 "Server error while verifying Basic "
195 "authentication");
197 } else {
198 http_send_response_401_basic(client_socket);
200 } else {
201 do_ws(client_socket, arguments->services, request,
202 ws_base_path_basic);
204 } else {
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);
216 exit(EXIT_SUCCESS);
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");
226 return;
229 if (request->method != HTTP_METHOD_POST) {
230 http_send_response_400(client_socket,
231 "First phase TOTP request must be POST");
232 return;
235 if (!http_client_authenticates(request)) {
236 http_send_response_401_otp(client_socket,
237 "totpsendsms",
238 "authentication.error.userIsNotAuthenticated",
239 "Client did not send any authentication header");
240 return;
243 switch(http_authenticate_basic(request,
244 arguments->username, arguments->password)) {
245 case HTTP_ERROR_SUCCESS: {
246 /* Find final URI */
247 char *uri = strstr(request->uri, "&uri=");
248 if (uri == NULL) {
249 http_send_response_400(client_socket,
250 "Missing uri parameter in Request URI");
251 return;
253 uri += 5;
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 "
259 "second OTP phase");
260 return;
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==?=",
268 location);
269 free(location);
271 break;
272 case HTTP_ERROR_CLIENT:
273 if (arguments->isds_deviations)
274 http_send_response_401_otp(client_socket,
275 "totpsendsms",
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. ");
281 else
282 http_send_response_403(client_socket);
283 break;
284 default:
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) {
294 switch (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");
308 return;
311 if (request->method != HTTP_METHOD_POST) {
312 http_send_response_400(client_socket,
313 "Second phase OTP request must be POST");
314 return;
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");
322 return;
325 switch(http_authenticate_otp(request,
326 arguments->username, arguments->password, arguments->otp)) {
327 case HTTP_ERROR_SUCCESS: {
328 /* Find final URI */
329 char *uri = strstr(request->uri, "&uri=");
330 if (uri == NULL) {
331 http_send_response_400(client_socket,
332 "Missing uri parameter in Request URI");
333 return;
335 uri += 5;
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");
341 return;
343 char *terminator = strchr(location, '&');
344 if (NULL != terminator)
345 *terminator = '\0';
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",
350 rand())) {
351 http_send_response_500(client_socket,
352 "Could not generate cookie value");
353 free(location);
354 return;
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,
363 /*NULL*/"/",
364 location);
365 free(location);
367 break;
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. ");
377 else
378 http_send_response_403(client_socket);
379 break;
380 default:
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) {
396 http_error error;
397 if (arguments == NULL) {
398 http_send_response_500(client_socket,
399 "Third argument of do_asws() is NULL");
400 return;
403 if (request->method != HTTP_METHOD_POST) {
404 http_send_response_400(client_socket,
405 "ASWS request must be POST");
406 return;
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");
414 return;
417 /* Try OTP */
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);
424 return;
427 if (AUTH_OTP_TIME == arguments->method) {
428 /* Try Basic */
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);
435 return;
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. ");
448 else
449 http_send_response_403(client_socket);
450 return;
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");
464 return;
467 if (request->method != HTTP_METHOD_GET) {
468 http_send_response_400(client_socket,
469 "OTP cookie invalidation request must be GET");
470 return;
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);
479 return;
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,
489 /*NULL*/"/",
490 NULL, 0, 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);
505 else
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) {
517 int client_socket;
518 const struct arguments_otp_authentication *arguments =
519 (const struct arguments_otp_authentication *) server_arguments;
520 struct http_request *request = NULL;
521 http_error error;
523 if (arguments == NULL) {
524 close(server_socket);
525 exit(EXIT_FAILURE);
528 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
529 fprintf(stderr, "Connection accepted\n");
530 error = http_read_request(client_socket, &request);
531 if (error) {
532 fprintf(stderr, "Error while reading request\n");
533 if (error == HTTP_ERROR_CLIENT)
534 http_send_response_400(client_socket, "Error in request");
535 else
536 http_send_response_500(client_socket, "Could not read request");
537 close(client_socket);
538 continue;
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,
560 ws_base_path_otp);
561 } else {
562 http_send_response_400(client_socket,
563 "Unknown path for OTP authenticating service");
565 } else {
566 if (!strcmp(request->uri, ws_path)) {
567 do_ws(client_socket, arguments->services, request,
568 ws_base_path_otp);
569 } else {
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);
582 exit(EXIT_SUCCESS);
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) {
593 int client_socket;
594 struct http_request *request = NULL;
595 http_error error;
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);
602 if (error) {
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);
607 continue;
610 http_send_response_503(client_socket, fault, strlen(fault),
611 soap_mime_type);
612 http_request_free(&request);
614 close(client_socket);
617 close(server_socket);
618 exit(EXIT_SUCCESS);
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
629 * usually contains:
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
633 * socket.
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
637 * as possible.
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) {
643 int server_socket;
645 if (server_address == NULL) {
646 set_server_error("start_server(): Got invalid server_address pointer");
647 return -1;
649 *server_address = NULL;
651 if (server_process == NULL) {
652 set_server_error("start_server(): Got invalid server_process pointer");
653 return -1;
656 server_socket = listen_on_socket();
657 if (server_socket == -1) {
658 set_server_error("Could not create listening socket");
659 return -1;
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");
666 return -1;
669 *server_process = fork();
670 if (*server_process == -1) {
671 close(server_socket);
672 set_server_error("Server could not been forked");
673 return -1;
676 if (*server_process == 0) {
677 server_implementation(server_socket, server_arguments);
678 /* Does not return */
681 return 0;
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");
690 return -1;
692 if (-1 == kill(server_process, SIGTERM)) {
693 set_server_error("Could not terminate server");
694 return -1;
696 if (-1 == waitpid(server_process, NULL, 0)) {
697 set_server_error("Could not wait for server termination");
698 return -1;
700 return 0;