test: Introduce isds_change_password
[libisds.git] / test / simline / server.c
blob16854894dbec8a8c717eb1adc0e64516e0b9c315
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 *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) {
49 int retval;
50 struct addrinfo hints;
51 struct addrinfo *addresses, *address;
52 int fd;
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);
58 if (retval) {
59 set_server_error("Could not resolve `localhost'");
60 return -1;
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);
72 if (retval == 0) {
73 freeaddrinfo(addresses);
74 return fd;
78 freeaddrinfo(addresses);
79 set_server_error("Could not start listening on TCP/localhost");
80 return -1;
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];
91 char *address = NULL;
93 if (-1 == getsockname(socket, (struct sockaddr *)&storage, &length)) {
94 set_server_error("Could not get address of server socket");
95 return NULL;
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");
102 return NULL;
105 if (-1 == test_asprintf(&address,
106 (strchr(host, ':') == NULL) ? "%s:%s" : "[%s]:%s",
107 host, service)) {
108 set_server_error("Could not format server address");
109 return NULL;
112 return 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");
125 return;
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");
133 return;
135 end_point += required_base_path_length;
138 soap(client_socket, ws_configuration, request->body, request->body_length,
139 end_point);
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) {
149 int client_socket;
150 const struct arguments_basic_authentication *arguments =
151 (const struct arguments_basic_authentication *) server_arguments;
152 struct http_request *request = NULL;
153 http_error error;
155 if (arguments == NULL) {
156 close(server_socket);
157 exit(EXIT_FAILURE);
160 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
161 fprintf(stderr, "Connection accepted\n");
162 error = http_read_request(client_socket, &request);
163 if (error) {
164 fprintf(stderr, "Error while reading request\n");
165 if (error == HTTP_ERROR_CLIENT)
166 http_send_response_400(client_socket, "Error in request");
167 else
168 http_send_response_500(client_socket,
169 "Error while reading request");
170 close(client_socket);
171 continue;
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,
182 ws_base_path_basic);
183 break;
184 case HTTP_ERROR_CLIENT:
185 if (arguments->isds_deviations)
186 http_send_response_401_basic(client_socket);
187 else
188 http_send_response_403(client_socket);
189 break;
190 default:
191 http_send_response_500(client_socket,
192 "Server error while verifying Basic "
193 "authentication");
195 } else {
196 http_send_response_401_basic(client_socket);
198 } else {
199 do_ws(client_socket, arguments->services, request,
200 ws_base_path_basic);
202 } else {
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);
214 exit(EXIT_SUCCESS);
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");
224 return;
227 if (request->method != HTTP_METHOD_POST) {
228 http_send_response_400(client_socket,
229 "First phase TOTP request must be POST");
230 return;
233 if (!http_client_authenticates(request)) {
234 http_send_response_401_otp(client_socket,
235 "totpsendsms",
236 "authentication.error.userIsNotAuthenticated",
237 "Client did not send any authentication header");
238 return;
241 switch(http_authenticate_basic(request,
242 arguments->username, arguments->password)) {
243 case HTTP_ERROR_SUCCESS: {
244 /* Find final URI */
245 char *uri = strstr(request->uri, "&uri=");
246 if (uri == NULL) {
247 http_send_response_400(client_socket,
248 "Missing uri parameter in Request URI");
249 return;
251 uri += 5;
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 "
257 "second OTP phase");
258 return;
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==?=",
266 location);
267 free(location);
269 break;
270 case HTTP_ERROR_CLIENT:
271 if (arguments->isds_deviations)
272 http_send_response_401_otp(client_socket,
273 "totpsendsms",
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. ");
279 else
280 http_send_response_403(client_socket);
281 break;
282 default:
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) {
292 switch (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");
306 return;
309 if (request->method != HTTP_METHOD_POST) {
310 http_send_response_400(client_socket,
311 "Second phase OTP request must be POST");
312 return;
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");
320 return;
323 switch(http_authenticate_otp(request,
324 arguments->username, arguments->password, arguments->otp)) {
325 case HTTP_ERROR_SUCCESS: {
326 /* Find final URI */
327 char *uri = strstr(request->uri, "&uri=");
328 if (uri == NULL) {
329 http_send_response_400(client_socket,
330 "Missing uri parameter in Request URI");
331 return;
333 uri += 5;
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");
339 return;
341 char *terminator = strchr(location, '&');
342 if (NULL != terminator)
343 *terminator = '\0';
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",
348 rand())) {
349 http_send_response_500(client_socket,
350 "Could not generate cookie value");
351 free(location);
352 return;
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,
361 /*NULL*/"/",
362 location);
363 free(location);
365 break;
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. ");
375 else
376 http_send_response_403(client_socket);
377 break;
378 default:
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");
391 return;
394 if (request->method != HTTP_METHOD_GET) {
395 http_send_response_400(client_socket,
396 "OTP cookie invalidation request must be GET");
397 return;
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);
406 return;
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,
416 /*NULL*/"/",
417 NULL, 0, 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);
432 else
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) {
444 int client_socket;
445 const struct arguments_otp_authentication *arguments =
446 (const struct arguments_otp_authentication *) server_arguments;
447 struct http_request *request = NULL;
448 http_error error;
450 if (arguments == NULL) {
451 close(server_socket);
452 exit(EXIT_FAILURE);
455 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
456 fprintf(stderr, "Connection accepted\n");
457 error = http_read_request(client_socket, &request);
458 if (error) {
459 fprintf(stderr, "Error while reading request\n");
460 if (error == HTTP_ERROR_CLIENT)
461 http_send_response_400(client_socket, "Error in request");
462 else
463 http_send_response_500(client_socket, "Could not read request");
464 close(client_socket);
465 continue;
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,
485 ws_base_path_otp);
486 } else {
487 http_send_response_400(client_socket,
488 "Unknown path for TOTP authenticating service");
490 } else {
491 if (!strcmp(request->uri, ws_path)) {
492 do_ws(client_socket, arguments->services, request,
493 ws_base_path_otp);
494 } else {
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);
507 exit(EXIT_SUCCESS);
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) {
518 int client_socket;
519 struct http_request *request = NULL;
520 http_error error;
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);
527 if (error) {
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);
532 continue;
535 http_send_response_503(client_socket, fault, strlen(fault),
536 soap_mime_type);
537 http_request_free(&request);
539 close(client_socket);
542 close(server_socket);
543 exit(EXIT_SUCCESS);
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
553 * socket.
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
557 * as possible.
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) {
562 int server_socket;
564 if (server_address == NULL) {
565 set_server_error("start_server(): Got invalid server_address pointer");
566 return -1;
568 *server_address = NULL;
570 if (server_process == NULL) {
571 set_server_error("start_server(): Got invalid server_process pointer");
572 return -1;
575 server_socket = listen_on_socket();
576 if (server_socket == -1) {
577 set_server_error("Could not create listening socket");
578 return -1;
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");
585 return -1;
588 *server_process = fork();
589 if (*server_process == -1) {
590 close(server_socket);
591 set_server_error("Server could not been forked");
592 return -1;
595 if (*server_process == 0) {
596 server_implementation(server_socket, server_arguments);
597 /* Does not return */
600 return 0;
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");
609 return -1;
611 if (-1 == kill(server_process, SIGTERM)) {
612 set_server_error("Could not terminate server");
613 return -1;
615 if (-1 == waitpid(server_process, NULL, 0)) {
616 set_server_error("Could not wait for server termination");
617 return -1;
619 return 0;