test: Check for end point path
[libisds.git] / test / simline / server.c
blobd5ef04baa05ea7cdf9cdea227625e329f90b1a90
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, const struct http_request *request,
118 const char *required_base_path) {
119 char *end_point = request->uri; /* Pointer to string in request */
121 if (request->method != HTTP_METHOD_POST) {
122 http_send_response_400(client_socket,
123 "Regular ISDS web service request must be POST");
124 return;
127 if (required_base_path != NULL) {
128 size_t required_base_path_length = strlen(required_base_path);
129 if (strncmp(end_point, required_base_path, required_base_path_length)) {
130 http_send_response_400(client_socket,
131 "Request sent to invalid path");
132 return;
134 end_point += required_base_path_length;
137 soap(client_socket, request->body, request->body_length, end_point);
141 /* Do the server protocol.
142 * @server_socket is listening TCP socket of the server
143 * @server_arguments is pointer to structure:
144 * Never returns. Terminates by exit(). */
145 void server_basic_authentication(int server_socket,
146 const void *server_arguments) {
147 int client_socket;
148 const struct arguments_basic_authentication *arguments =
149 (const struct arguments_basic_authentication *) server_arguments;
150 struct http_request *request = NULL;
151 http_error error;
153 if (arguments == NULL) {
154 close(server_socket);
155 exit(EXIT_FAILURE);
158 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
159 fprintf(stderr, "Connection accepted\n");
160 error = http_read_request(client_socket, &request);
161 if (error) {
162 fprintf(stderr, "Error while reading request\n");
163 if (error == HTTP_ERROR_CLIENT)
164 http_send_response_400(client_socket, "Error in request");
165 else
166 http_send_response_500(client_socket,
167 "Error while reading request");
168 close(client_socket);
169 continue;
172 if (request->method == HTTP_METHOD_POST) {
173 /* Only POST requests are used in Basic authentication mode */
174 if (arguments->username != NULL) {
175 if (http_client_authenticates(request)) {
176 switch(http_authenticate_basic(request,
177 arguments->username, arguments->password)) {
178 case HTTP_ERROR_SUCCESS:
179 do_ws(client_socket, request, ws_base_path_basic);
180 break;
181 case HTTP_ERROR_CLIENT:
182 if (arguments->isds_deviations)
183 http_send_response_401_basic(client_socket);
184 else
185 http_send_response_403(client_socket);
186 break;
187 default:
188 http_send_response_500(client_socket,
189 "Server error while verifying Basic "
190 "authentication");
192 } else {
193 http_send_response_401_basic(client_socket);
195 } else {
196 do_ws(client_socket, request, ws_base_path_basic);
198 } else {
199 /* HTTP method unsupported per ISDS specification */
200 http_send_response_400(client_socket,
201 "Only POST method is allowed");
203 http_request_free(&request);
206 close(client_socket);
209 close(server_socket);
210 exit(EXIT_SUCCESS);
214 /* Process first phase of TOTP request */
215 static void do_as_sendsms(int client_socket, const struct http_request *request,
216 const struct arguments_otp_authentication *arguments) {
217 if (arguments == NULL) {
218 http_send_response_500(client_socket,
219 "Third argument of do_as_sendsms() is NULL");
220 return;
223 if (request->method != HTTP_METHOD_POST) {
224 http_send_response_400(client_socket,
225 "First phase TOTP request must be POST");
226 return;
229 if (!http_client_authenticates(request)) {
230 http_send_response_401_otp(client_socket,
231 "totpsendsms",
232 "authentication.error.userIsNotAuthenticated",
233 "Client did not send any authentication header");
234 return;
237 switch(http_authenticate_basic(request,
238 arguments->username, arguments->password)) {
239 case HTTP_ERROR_SUCCESS: {
240 /* Find final URI */
241 char *uri = strstr(request->uri, "&uri=");
242 if (uri == NULL) {
243 http_send_response_400(client_socket,
244 "Missing uri parameter in Request URI");
245 return;
247 uri += 5;
248 /* Build new location for second OTP phase */
249 char *location = NULL;
250 if (-1 == test_asprintf(&location, "%s%s", as_path_dontsendsms, uri)) {
251 http_send_response_500(client_socket,
252 "Could not build new localtion for "
253 "second OTP phase");
254 return;
256 char *terminator = strchr(uri, '&');
257 if (NULL != terminator)
258 location[strlen(as_path_dontsendsms) + (uri - terminator)] = '\0';
259 http_send_response_302_totp(client_socket,
260 "authentication.info.totpSended",
261 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
262 location);
263 free(location);
265 break;
266 case HTTP_ERROR_CLIENT:
267 if (arguments->isds_deviations)
268 http_send_response_401_otp(client_socket,
269 "totpsendsms",
270 "authentication.error.userIsNotAuthenticated",
271 " Retry: Bad user name or password in first OTP phase.\r\n"
272 " This is very long header\r\n"
273 " which should span to more lines.\r\n"
274 " Surrounding LWS are meaning-less. ");
275 else
276 http_send_response_403(client_socket);
277 break;
278 default:
279 http_send_response_500(client_socket,
280 "Could not verify Basic authentication");
285 /* Return static string representation of HTTP OTP authentication method.
286 * Or NULL in case of error. */
287 static const char *auth_otp_method2string(enum auth_otp_method method) {
288 switch (method) {
289 case AUTH_OTP_HMAC: return "hotp";
290 case AUTH_OTP_TIME: return "totp";
291 default: return NULL;
296 /* Process second phase of OTP request */
297 static void do_as_phase_two(int client_socket, const struct http_request *request,
298 const struct arguments_otp_authentication *arguments) {
299 if (arguments == NULL) {
300 http_send_response_500(client_socket,
301 "Third argument of do_as_phase_two() is NULL");
302 return;
305 if (request->method != HTTP_METHOD_POST) {
306 http_send_response_400(client_socket,
307 "Second phase OTP request must be POST");
308 return;
311 if (!http_client_authenticates(request)) {
312 http_send_response_401_otp(client_socket,
313 auth_otp_method2string(arguments->method),
314 "authentication.error.userIsNotAuthenticated",
315 "Client did not send any authentication header");
316 return;
319 switch(http_authenticate_otp(request,
320 arguments->username, arguments->password, arguments->otp)) {
321 case HTTP_ERROR_SUCCESS: {
322 /* Find final URI */
323 char *uri = strstr(request->uri, "&uri=");
324 if (uri == NULL) {
325 http_send_response_400(client_socket,
326 "Missing uri parameter in Request URI");
327 return;
329 uri += 5;
330 /* Build new location for final request */
331 char *location = NULL;
332 if (NULL == (location = strdup(uri))) {
333 http_send_response_500(client_socket,
334 "Could not build new location for final request");
335 return;
337 char *terminator = strchr(location, '&');
338 if (NULL != terminator)
339 *terminator = '\0';
340 /* Generate pseudo-random cookie value. This is to prevent
341 * client from reusing the cookie accidentally. We use the
342 * same seed to get reproducible tests. */
343 if (-1 == test_asprintf(&authorization_cookie_value, "%d",
344 rand())) {
345 http_send_response_500(client_socket,
346 "Could not generate cookie value");
347 free(location);
348 return;
350 /* XXX: Add Path parameter to cookie, otherwise
351 * different paths will not match.
352 * FIXME: Domain argument does not work with cURL. Report a bug. */
353 http_send_response_302_cookie(client_socket,
354 authorization_cookie_name,
355 authorization_cookie_value,
356 /*http_find_host(request)*/NULL,
357 /*NULL*/"/",
358 location);
359 free(location);
361 break;
362 case HTTP_ERROR_CLIENT:
363 if (arguments->isds_deviations)
364 http_send_response_401_otp(client_socket,
365 auth_otp_method2string(arguments->method),
366 "authentication.error.userIsNotAuthenticated",
367 " Retry: Bad user name or password in second OTP phase.\r\n"
368 " This is very long header\r\n"
369 " which should span to more lines.\r\n"
370 " Surrounding LWS are meaning-less. ");
371 else
372 http_send_response_403(client_socket);
373 break;
374 default:
375 http_send_response_500(client_socket,
376 "Could not verify OTP authentication");
381 /* Process OTP session cookie invalidation request */
382 static void do_as_logout(int client_socket, const struct http_request *request,
383 const struct arguments_otp_authentication *arguments) {
384 if (arguments == NULL) {
385 http_send_response_500(client_socket,
386 "Third argument of do_as_logout() is NULL");
387 return;
390 if (request->method != HTTP_METHOD_GET) {
391 http_send_response_400(client_socket,
392 "OTP cookie invalidation request must be GET");
393 return;
396 const char *received_cookie =
397 http_find_cookie(request, authorization_cookie_name);
399 if (authorization_cookie_value == NULL || received_cookie == NULL ||
400 strcmp(authorization_cookie_value, received_cookie)) {
401 http_send_response_403(client_socket);
402 return;
405 /* XXX: Add Path parameter to cookie, otherwise
406 * different paths will not match.
407 * FIXME: Domain argument does not work with cURL. Report a bug. */
408 http_send_response_200_cookie(client_socket,
409 authorization_cookie_name,
411 /*http_find_host(request)*/ NULL,
412 /*NULL*/"/",
413 NULL, 0, NULL);
417 /* Process ISDS WS ping authorized by cookie */
418 static void do_ws_with_cookie(int client_socket, const struct http_request *request,
419 const struct arguments_otp_authentication *arguments,
420 const char *valid_base_path) {
421 const char *received_cookie =
422 http_find_cookie(request, authorization_cookie_name);
424 if (authorization_cookie_value != NULL && received_cookie != NULL &&
425 !strcmp(authorization_cookie_value, received_cookie))
426 do_ws(client_socket, request, valid_base_path);
427 else
428 http_send_response_403(client_socket);
432 /* Do the server protocol with OTP authentication.
433 * @server_socket is listening TCP socket of the server
434 * @server_arguments is pointer to structure arguments_otp_authentication. It
435 * selects OTP method to enable.
436 * Never returns. Terminates by exit(). */
437 void server_otp_authentication(int server_socket,
438 const void *server_arguments) {
439 int client_socket;
440 const struct arguments_otp_authentication *arguments =
441 (const struct arguments_otp_authentication *) server_arguments;
442 struct http_request *request = NULL;
443 http_error error;
445 if (arguments == NULL) {
446 close(server_socket);
447 exit(EXIT_FAILURE);
450 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
451 fprintf(stderr, "Connection accepted\n");
452 error = http_read_request(client_socket, &request);
453 if (error) {
454 fprintf(stderr, "Error while reading request\n");
455 if (error == HTTP_ERROR_CLIENT)
456 http_send_response_400(client_socket, "Error in request");
457 else
458 http_send_response_500(client_socket, "Could not read request");
459 close(client_socket);
460 continue;
463 if (arguments->username != NULL) {
464 if (arguments->method == AUTH_OTP_HMAC &&
465 !strncmp(request->uri, as_path_hotp, strlen(as_path_hotp))) {
466 do_as_phase_two(client_socket, request, arguments);
467 } else if (arguments->method == AUTH_OTP_TIME &&
468 !strncmp(request->uri, as_path_sendsms,
469 strlen(as_path_sendsms))) {
470 do_as_sendsms(client_socket, request, arguments);
471 } else if (arguments->method == AUTH_OTP_TIME &&
472 !strncmp(request->uri, as_path_dontsendsms,
473 strlen(as_path_dontsendsms))) {
474 do_as_phase_two(client_socket, request, arguments);
475 } else if (!strncmp(request->uri, as_path_logout,
476 strlen(as_path_logout))) {
477 do_as_logout(client_socket, request, arguments);
478 } else if (!strcmp(request->uri, ws_path)) {
479 do_ws_with_cookie(client_socket, request, arguments,
480 ws_base_path_otp);
481 } else {
482 http_send_response_400(client_socket,
483 "Unknown path for TOTP authenticating service");
485 } else {
486 if (!strcmp(request->uri, ws_path)) {
487 do_ws(client_socket, request, ws_base_path_otp);
488 } else {
489 http_send_response_400(client_socket,
490 "Unknown path for TOTP authenticating service");
493 http_request_free(&request);
496 close(client_socket);
499 close(server_socket);
500 free(authorization_cookie_value);
501 exit(EXIT_SUCCESS);
505 /* Implementation of server that is out of order.
506 * It always sends back SOAP Fault with HTTP error 503.
507 * @server_socket is listening TCP socket of the server
508 * @server_arguments is ununsed pointer
509 * Never returns. Terminates by exit(). */
510 void server_out_of_order(int server_socket,
511 const void *server_arguments) {
512 int client_socket;
513 struct http_request *request = NULL;
514 http_error error;
515 const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
516 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>";
518 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
519 fprintf(stderr, "Connection accepted\n");
520 error = http_read_request(client_socket, &request);
521 if (error) {
522 fprintf(stderr, "Error while reading request\n");
523 if (error == HTTP_ERROR_CLIENT)
524 http_send_response_400(client_socket, "Error in request");
525 close(client_socket);
526 continue;
529 http_send_response_503(client_socket, fault, strlen(fault),
530 soap_mime_type);
531 http_request_free(&request);
533 close(client_socket);
536 close(server_socket);
537 exit(EXIT_SUCCESS);
541 /* Start sever in separate process.
542 * @server_process is PID of forked server
543 * @server_address is automatically allocated TCP address of listening server
544 * @username sets required user name server has to require. Set NULL to
545 * disable HTTP authentication.
546 * @password sets required password server has to require
547 * socket.
548 * @isds_deviations is flag to set conformance level. If false, server is
549 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
550 * specification. Otherwise server mimics real ISDS implementation as much
551 * as possible.
552 * @return -1 in case of error. */
553 int start_server(pid_t *server_process, char **server_address,
554 void (*server_implementation)(int, const void *),
555 const void *server_arguments) {
556 int server_socket;
558 if (server_address == NULL) {
559 set_server_error("start_server(): Got invalid server_address pointer");
560 return -1;
562 *server_address = NULL;
564 if (server_process == NULL) {
565 set_server_error("start_server(): Got invalid server_process pointer");
566 return -1;
569 server_socket = listen_on_socket();
570 if (server_socket == -1) {
571 set_server_error("Could not create listening socket");
572 return -1;
575 *server_address = socket2address(server_socket);
576 if (*server_address == NULL) {
577 close(server_socket);
578 set_server_error("Could not format address of listening address");
579 return -1;
582 *server_process = fork();
583 if (*server_process == -1) {
584 close(server_socket);
585 set_server_error("Server could not been forked");
586 return -1;
589 if (*server_process == 0) {
590 server_implementation(server_socket, server_arguments);
591 /* Does not return */
594 return 0;
598 /* Kill the server process.
599 * Return -1 in case of error. */
600 int stop_server(pid_t server_process) {
601 if (server_process <= 0) {
602 set_server_error("Invalid server PID to kill");
603 return -1;
605 if (-1 == kill(server_process, SIGTERM)) {
606 set_server_error("Could not terminate server");
607 return -1;
609 if (-1 == waitpid(server_process, NULL, 0)) {
610 set_server_error("Could not wait for server termination");
611 return -1;
613 return 0;