test: OTP: Fix a typo
[libisds.git] / test / simline / server.c
bloba5a3be0e1e056cce23b549fe013a24be78193c25
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"
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netdb.h>
18 #include <string.h>
19 #include <signal.h>
20 #include <unistd.h>
21 #include <wait.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) {
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 authorized by cookie */
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");
121 return;
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) {
134 int client_socket;
135 const struct arguments_basic_authentication *arguments =
136 (const struct arguments_basic_authentication *) server_arguments;
137 struct http_request *request = NULL;
138 http_error error;
140 if (arguments == NULL) {
141 close(server_socket);
142 exit(EXIT_FAILURE);
145 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
146 fprintf(stderr, "Connection accepted\n");
147 error = http_read_request(client_socket, &request);
148 if (error) {
149 fprintf(stderr, "Error while reading request\n");
150 if (error == HTTP_ERROR_CLIENT)
151 http_send_response_400(client_socket, "Error in request");
152 else
153 http_send_response_500(client_socket);
154 close(client_socket);
155 continue;
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 http_send_response_200(client_socket,
166 pong, strlen(pong), soap_mime_type);
167 break;
168 case HTTP_ERROR_CLIENT:
169 if (arguments->isds_deviations)
170 http_send_response_401_basic(client_socket);
171 else
172 http_send_response_403(client_socket);
173 break;
174 default:
175 http_send_response_500(client_socket);
177 } else {
178 http_send_response_401_basic(client_socket);
180 } else {
181 http_send_response_200(client_socket,
182 pong, strlen(pong), soap_mime_type);
184 } else {
185 /* HTTP method unsupported per ISDS specification */
186 http_send_response_400(client_socket,
187 "Only POST method is allowed");
189 http_request_free(&request);
192 close(client_socket);
195 close(server_socket);
196 exit(EXIT_SUCCESS);
200 /* Process first phase of TOTP request */
201 static void do_as_sendsms(int client_socket, const struct http_request *request,
202 const struct arguments_otp_authentication *arguments) {
203 if (arguments == NULL) {
204 http_send_response_500(client_socket);
205 return;
208 if (request->method != HTTP_METHOD_POST) {
209 http_send_response_400(client_socket,
210 "First phase TOTP request must be POST");
211 return;
214 if (!http_client_authenticates(request)) {
215 http_send_response_401_otp(client_socket,
216 "totpsendsms",
217 "authentication.error.userIsNotAuthenticated",
218 "Client did not send any authentication header");
219 return;
222 switch(http_authenticate_basic(request,
223 arguments->username, arguments->password)) {
224 case HTTP_ERROR_SUCCESS: {
225 /* Find final URI */
226 char *uri = strstr(request->uri, "&uri=");
227 if (uri == NULL) {
228 http_send_response_400(client_socket,
229 "Missing uri parameter in Request URI");
230 return;
232 uri += 5;
233 /* Build new location for second OTP phase */
234 char *location = NULL;
235 if (-1 == test_asprintf(&location, "%s%s", as_path_dontsendsms, uri)) {
236 http_send_response_500(client_socket);
237 return;
239 char *terminator = strchr(uri, '&');
240 if (NULL != terminator)
241 location[strlen(as_path_dontsendsms) + (uri - terminator)] = '\0';
242 http_send_response_302_totp(client_socket,
243 "authentication.info.totpSended",
244 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
245 location);
246 free(location);
248 break;
249 case HTTP_ERROR_CLIENT:
250 if (arguments->isds_deviations)
251 http_send_response_401_otp(client_socket,
252 "totpsendsms",
253 "authentication.error.userIsNotAuthenticated",
254 " Retry: Bad user name or password in first OTP phase.\r\n"
255 " This is very long header\r\n"
256 " which should span to more lines.\r\n"
257 " Surrounding LWS are meaning-less. ");
258 else
259 http_send_response_403(client_socket);
260 break;
261 default:
262 http_send_response_500(client_socket);
267 /* Return static string representation of HTTP OTP authentication method.
268 * Or NULL in case of error. */
269 static const char *auth_otp_method2string(enum auth_otp_method method) {
270 switch (method) {
271 case AUTH_OTP_HASH: return "hotp";
272 case AUTH_OTP_TIME: return "totp";
273 default: return NULL;
278 /* Process second phase of OTP request */
279 static void do_as_phase_two(int client_socket, const struct http_request *request,
280 const struct arguments_otp_authentication *arguments) {
281 if (arguments == NULL) {
282 http_send_response_500(client_socket);
283 return;
286 if (request->method != HTTP_METHOD_POST) {
287 http_send_response_400(client_socket,
288 "Second phase OTP request must be POST");
289 return;
292 if (!http_client_authenticates(request)) {
293 http_send_response_401_otp(client_socket,
294 auth_otp_method2string(arguments->method),
295 "authentication.error.userIsNotAuthenticated",
296 "Client did not send any authentication header");
297 return;
300 switch(http_authenticate_otp(request,
301 arguments->username, arguments->password, arguments->otp)) {
302 case HTTP_ERROR_SUCCESS: {
303 /* Find final URI */
304 char *uri = strstr(request->uri, "&uri=");
305 if (uri == NULL) {
306 http_send_response_400(client_socket,
307 "Missing uri parameter in Request URI");
308 return;
310 uri += 5;
311 /* Build new location for final request */
312 char *location = NULL;
313 if (NULL == (location = strdup(uri))) {
314 http_send_response_500(client_socket);
315 return;
317 char *terminator = strchr(location, '&');
318 if (NULL != terminator)
319 *terminator = '\0';
320 /* Generate pseudo-random cookie value. This is to prevent
321 * client from reusing the cookie accidentally. We use the
322 * same seed to get reproducible tests. */
323 if (-1 == test_asprintf(&authorization_cookie_value, "%d",
324 rand())) {
325 http_send_response_500(client_socket);
326 free(location);
327 return;
329 /* XXX: Add Path parameter to cookie, otherwise
330 * different paths will not match.
331 * FIXME: Domain argument does not work with cURL. Report a bug. */
332 http_send_response_302_cookie(client_socket,
333 authorization_cookie_name,
334 authorization_cookie_value,
335 /*http_find_host(request)*/NULL,
336 /*NULL*/"/",
337 location);
338 free(location);
340 break;
341 case HTTP_ERROR_CLIENT:
342 if (arguments->isds_deviations)
343 http_send_response_401_otp(client_socket,
344 auth_otp_method2string(arguments->method),
345 "authentication.error.userIsNotAuthenticated",
346 " Retry: Bad user name or password in second OTP phase.\r\n"
347 " This is very long header\r\n"
348 " which should span to more lines.\r\n"
349 " Surrounding LWS are meaning-less. ");
350 else
351 http_send_response_403(client_socket);
352 break;
353 default:
354 http_send_response_500(client_socket);
359 /* Process OTP session cookie invalidation request */
360 static void do_as_logout(int client_socket, const struct http_request *request,
361 const struct arguments_otp_authentication *arguments) {
362 if (arguments == NULL) {
363 http_send_response_500(client_socket);
364 return;
367 if (request->method != HTTP_METHOD_GET) {
368 http_send_response_400(client_socket,
369 "OTP cookie invalidation request must be GET");
370 return;
373 const char *received_cookie =
374 http_find_cookie(request, authorization_cookie_name);
376 if (authorization_cookie_value == NULL || received_cookie == NULL ||
377 strcmp(authorization_cookie_value, received_cookie)) {
378 http_send_response_403(client_socket);
379 return;
382 /* XXX: Add Path parameter to cookie, otherwise
383 * different paths will not match.
384 * FIXME: Domain argument does not work with cURL. Report a bug. */
385 http_send_response_200_cookie(client_socket,
386 authorization_cookie_name,
388 /*http_find_host(request)*/ NULL,
389 /*NULL*/"/",
390 NULL, 0, NULL);
394 /* Process ISDS WS ping authorized by cookie */
395 static void do_ws_with_cookie(int client_socket, const struct http_request *request,
396 const struct arguments_otp_authentication *arguments) {
397 const char *received_cookie =
398 http_find_cookie(request, authorization_cookie_name);
400 if (authorization_cookie_value != NULL && received_cookie != NULL &&
401 !strcmp(authorization_cookie_value, received_cookie))
402 do_ws(client_socket, request);
403 else
404 http_send_response_403(client_socket);
408 /* Do the server protocol with OTP authentication.
409 * @server_socket is listening TCP socket of the server
410 * @server_arguments is pointer to structure arguments_otp_authentication. It
411 * selects OTP method to enable.
412 * Never returns. Terminates by exit(). */
413 void server_otp_authentication(int server_socket,
414 const void *server_arguments) {
415 int client_socket;
416 const struct arguments_otp_authentication *arguments =
417 (const struct arguments_otp_authentication *) server_arguments;
418 struct http_request *request = NULL;
419 http_error error;
421 if (arguments == NULL) {
422 close(server_socket);
423 exit(EXIT_FAILURE);
426 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
427 fprintf(stderr, "Connection accepted\n");
428 error = http_read_request(client_socket, &request);
429 if (error) {
430 fprintf(stderr, "Error while reading request\n");
431 if (error == HTTP_ERROR_CLIENT)
432 http_send_response_400(client_socket, "Error in request");
433 else
434 http_send_response_500(client_socket);
435 close(client_socket);
436 continue;
439 if (arguments->username != NULL) {
440 if (arguments->method == AUTH_OTP_HASH &&
441 !strncmp(request->uri, as_path_hotp, strlen(as_path_hotp))) {
442 do_as_phase_two(client_socket, request, arguments);
443 } else if (arguments->method == AUTH_OTP_TIME &&
444 !strncmp(request->uri, as_path_sendsms,
445 strlen(as_path_sendsms))) {
446 do_as_sendsms(client_socket, request, arguments);
447 } else if (arguments->method == AUTH_OTP_TIME &&
448 !strncmp(request->uri, as_path_dontsendsms,
449 strlen(as_path_dontsendsms))) {
450 do_as_phase_two(client_socket, request, arguments);
451 } else if (!strncmp(request->uri, as_path_logout,
452 strlen(as_path_logout))) {
453 do_as_logout(client_socket, request, arguments);
454 } else if (!strcmp(request->uri, ws_path)) {
455 do_ws_with_cookie(client_socket, request, arguments);
456 } else {
457 http_send_response_400(client_socket,
458 "Unknown path for TOTP authenticating service");
460 } else {
461 if (!strcmp(request->uri, ws_path)) {
462 do_ws(client_socket, request);
463 } else {
464 http_send_response_400(client_socket,
465 "Unknown path for TOTP authenticating service");
468 http_request_free(&request);
471 close(client_socket);
474 close(server_socket);
475 free(authorization_cookie_value);
476 exit(EXIT_SUCCESS);
480 /* Implementation of server that is out of order.
481 * It always sends back SOAP Fault with HTTP error 503.
482 * @server_socket is listening TCP socket of the server
483 * @server_arguments is ununsed pointer
484 * Never returns. Terminates by exit(). */
485 void server_out_of_order(int server_socket,
486 const void *server_arguments) {
487 int client_socket;
488 struct http_request *request = NULL;
489 http_error error;
490 const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
491 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>";
493 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
494 fprintf(stderr, "Connection accepted\n");
495 error = http_read_request(client_socket, &request);
496 if (error) {
497 fprintf(stderr, "Error while reading request\n");
498 if (error == HTTP_ERROR_CLIENT)
499 http_send_response_400(client_socket, "Error in request");
500 close(client_socket);
501 continue;
504 http_send_response_503(client_socket, fault, strlen(fault),
505 soap_mime_type);
506 http_request_free(&request);
508 close(client_socket);
511 close(server_socket);
512 exit(EXIT_SUCCESS);
516 /* Start sever in separate process.
517 * @server_process is PID of forked server
518 * @server_address is automatically allocated TCP address of listening server
519 * @username sets required user name server has to require. Set NULL to
520 * disable HTTP authentication.
521 * @password sets required password server has to require
522 * socket.
523 * @isds_deviations is flag to set conformance level. If false, server is
524 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
525 * specification. Otherwise server mimics real ISDS implementation as much
526 * as possible.
527 * @return -1 in case of error. */
528 int start_server(pid_t *server_process, char **server_address,
529 void (*server_implementation)(int, const void *),
530 const void *server_arguments) {
531 int server_socket;
533 if (server_address == NULL) {
534 set_server_error("start_server(): Got invalid server_address pointer");
535 return -1;
537 *server_address = NULL;
539 if (server_process == NULL) {
540 set_server_error("start_server(): Got invalid server_process pointer");
541 return -1;
544 server_socket = listen_on_socket();
545 if (server_socket == -1) {
546 set_server_error("Could not create listening socket");
547 return -1;
550 *server_address = socket2address(server_socket);
551 if (*server_address == NULL) {
552 close(server_socket);
553 set_server_error("Could not format address of listening address");
554 return -1;
557 *server_process = fork();
558 if (*server_process == -1) {
559 close(server_socket);
560 set_server_error("Server could not been forked");
561 return -1;
564 if (*server_process == 0) {
565 server_implementation(server_socket, server_arguments);
566 /* Does not return */
569 return 0;
573 /* Kill the server process.
574 * Return -1 in case of error. */
575 int stop_server(pid_t server_process) {
576 if (server_process <= 0) {
577 set_server_error("Invalid server PID to kill");
578 return -1;
580 if (-1 == kill(server_process, SIGTERM)) {
581 set_server_error("Could not terminate server");
582 return -1;
584 if (-1 == waitpid(server_process, NULL, 0)) {
585 set_server_error("Could not wait for server termination");
586 return -1;
588 return 0;