test: starting server returns URL instead of socket address
[libisds.git] / test / simline / server.c
blob9a526aa47c9de8c6040d8f4501dd56e8f1753a9c
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 <errno.h> /* For EINTR */
19 #include <netdb.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <unistd.h>
23 #include <wait.h>
24 #include <gnutls/gnutls.h>
25 #include <gnutls/x509.h>
27 char *server_error = NULL;
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 *asws_path = "/asws/changePassword";
34 static const char *ws_path = "/apps/DS/dz";
36 static const char *ws_base_path_basic = "/";
37 static const char *ws_base_path_otp = "/apps/";
39 static const char *authorization_cookie_name = "IPCZ-X-COOKIE";
40 static char *authorization_cookie_value = NULL;
42 /* TLS stuff */
43 static gnutls_certificate_credentials_t x509_credentials;
44 static gnutls_priority_t priority_cache;
45 static gnutls_dh_params_t dh_parameters;
46 static const char *client_required_dn = NULL;
48 /* Save error message if not yet set. The message will be duplicated.
49 * @message is printf(3) formatting string. */
50 void set_server_error(const char *message, ...) {
51 if (server_error == NULL) {
52 va_list ap;
53 va_start(ap, message);
54 test_vasprintf(&server_error, message, ap);
55 va_end(ap);
60 /* Creates listening TCP socket on localhost.
61 * Returns the socket descriptor or -1. */
62 int listen_on_socket(void) {
63 int retval;
64 struct addrinfo hints;
65 struct addrinfo *addresses, *address;
66 int fd;
68 memset(&hints, 0, sizeof(hints));
69 hints.ai_family = AF_UNSPEC;
70 hints.ai_socktype = SOCK_STREAM;
71 retval = getaddrinfo("localhost", NULL, &hints, &addresses);
72 if (retval) {
73 set_server_error("Could not resolve `localhost'");
74 return -1;
77 for (address = addresses; address != NULL; address = address->ai_next) {
78 fd = socket(address->ai_family, address->ai_socktype,
79 address->ai_protocol);
80 if (fd == -1) continue;
82 retval = bind(fd, address->ai_addr, address->ai_addrlen);
83 if (retval != 0) continue;
85 retval = listen(fd, 0);
86 if (retval == 0) {
87 freeaddrinfo(addresses);
88 return fd;
92 freeaddrinfo(addresses);
93 set_server_error("Could not start listening on TCP/localhost");
94 return -1;
98 /* Format socket address as printable string.
99 * @return allocated string or NULL in case of error. */
100 char *socket2address(int socket) {
101 struct sockaddr_storage storage;
102 socklen_t length = (socklen_t) sizeof(storage);
103 char host[NI_MAXHOST];
104 char service[NI_MAXSERV];
105 char *address = NULL;
107 if (-1 == getsockname(socket, (struct sockaddr *)&storage, &length)) {
108 set_server_error("Could not get address of server socket");
109 return NULL;
112 if (0 != getnameinfo((struct sockaddr *)&storage, length,
113 host, sizeof(host), service, sizeof(service),
114 NI_NUMERICHOST|NI_NUMERICSERV)) {
115 set_server_error("Could not resolve address of server socket");
116 return NULL;
119 if (-1 == test_asprintf(&address,
120 (strchr(host, ':') == NULL) ? "%s:%s" : "[%s]:%s",
121 host, service)) {
122 set_server_error("Could not format server address");
123 return NULL;
126 return address;
130 /* Format HTTP(s) socket address as printable URL string.
131 * @socket is listening TCP socket of HTTP server
132 * @tls is true for HTTPS, false for plain HTTP
133 * @return allocated string or NULL in case of error. */
134 static char *socket2url(int socket, _Bool tls) {
135 char *socket_address = NULL;
136 char *address = NULL;
138 socket_address = socket2address(socket);
139 if (NULL == socket_address) {
140 set_server_error("Could not format server address");
141 free(socket_address);
142 return NULL;
145 if (-1 == test_asprintf(&address, "%s://%s/",
146 (tls) ? "https" : "http", socket_address)) {
147 set_server_error("Could not format server address");
148 free(socket_address);
149 return NULL;
152 free(socket_address);
153 return address;
157 /* Process ISDS web service */
158 static void do_ws(const struct http_connection *connection,
159 const struct service_configuration *ws_configuration,
160 const struct http_request *request, const char *required_base_path) {
161 char *end_point = request->uri; /* Pointer to string in the request */
163 if (request->method != HTTP_METHOD_POST) {
164 http_send_response_400(connection,
165 "Regular ISDS web service request must be POST");
166 return;
169 if (required_base_path != NULL) {
170 size_t required_base_path_length = strlen(required_base_path);
171 if (strncmp(end_point, required_base_path, required_base_path_length)) {
172 http_send_response_400(connection, "Request sent to invalid path");
173 return;
175 end_point += required_base_path_length;
178 soap(connection, ws_configuration, request->body, request->body_length,
179 end_point);
183 /* Do the server protocol.
184 * @connection is HTTP connection
185 * @server_arguments is pointer to structure:
186 * @request is parsed HTTP client request
187 * @return 0 to accept new client, return -1 in case of fatal error. */
188 int server_basic_authentication(const struct http_connection *connection,
189 const void *server_arguments, const struct http_request *request) {
190 const struct arguments_basic_authentication *arguments =
191 (const struct arguments_basic_authentication *) server_arguments;
193 if (NULL == arguments || NULL == request) {
194 return -1;
197 if (request->method == HTTP_METHOD_POST) {
198 /* Only POST requests are used in Basic authentication mode */
199 if (arguments->username != NULL) {
200 if (http_client_authenticates(request)) {
201 switch(http_authenticate_basic(request,
202 arguments->username, arguments->password)) {
203 case HTTP_ERROR_SUCCESS:
204 do_ws(connection, arguments->services, request,
205 ws_base_path_basic);
206 break;
207 case HTTP_ERROR_CLIENT:
208 if (arguments->isds_deviations)
209 http_send_response_401_basic(connection);
210 else
211 http_send_response_403(connection);
212 break;
213 default:
214 http_send_response_500(connection,
215 "Server error while verifying Basic "
216 "authentication");
218 } else {
219 http_send_response_401_basic(connection);
221 } else {
222 do_ws(connection, arguments->services, request,
223 ws_base_path_basic);
225 } else {
226 /* HTTP method unsupported per ISDS specification */
227 http_send_response_400(connection,
228 "Only POST method is allowed");
231 return 0;
235 /* Process first phase of TOTP request */
236 static void do_as_sendsms(const struct http_connection *connection,
237 const struct http_request *request,
238 const struct arguments_otp_authentication *arguments) {
239 if (arguments == NULL) {
240 http_send_response_500(connection,
241 "Third argument of do_as_sendsms() is NULL");
242 return;
245 if (request->method != HTTP_METHOD_POST) {
246 http_send_response_400(connection,
247 "First phase TOTP request must be POST");
248 return;
251 if (!http_client_authenticates(request)) {
252 http_send_response_401_otp(connection,
253 "totpsendsms",
254 "authentication.error.userIsNotAuthenticated",
255 "Client did not send any authentication header");
256 return;
259 switch(http_authenticate_basic(request,
260 arguments->username, arguments->password)) {
261 case HTTP_ERROR_SUCCESS: {
262 /* Find final URI */
263 char *uri = strstr(request->uri, "&uri=");
264 if (uri == NULL) {
265 http_send_response_400(connection,
266 "Missing uri parameter in Request URI");
267 return;
269 uri += 5;
270 /* Build new location for second OTP phase */
271 char *location = NULL;
272 if (-1 == test_asprintf(&location, "%s%s", as_path_dontsendsms, uri)) {
273 http_send_response_500(connection,
274 "Could not build new localtion for "
275 "second OTP phase");
276 return;
278 char *terminator = strchr(uri, '&');
279 if (NULL != terminator)
280 location[strlen(as_path_dontsendsms) + (uri - terminator)] = '\0';
281 http_send_response_302_totp(connection,
282 "authentication.info.totpSended",
283 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
284 location);
285 free(location);
287 break;
288 case HTTP_ERROR_CLIENT:
289 if (arguments->isds_deviations)
290 http_send_response_401_otp(connection,
291 "totpsendsms",
292 "authentication.error.userIsNotAuthenticated",
293 " Retry: Bad user name or password in first OTP phase.\r\n"
294 " This is very long header\r\n"
295 " which should span to more lines.\r\n"
296 " Surrounding LWS are meaning-less. ");
297 else
298 http_send_response_403(connection);
299 break;
300 default:
301 http_send_response_500(connection,
302 "Could not verify Basic authentication");
307 /* Return static string representation of HTTP OTP authentication method.
308 * Or NULL in case of error. */
309 static const char *auth_otp_method2string(enum auth_otp_method method) {
310 switch (method) {
311 case AUTH_OTP_HMAC: return "hotp";
312 case AUTH_OTP_TIME: return "totp";
313 default: return NULL;
318 /* Process second phase of OTP request */
319 static void do_as_phase_two(const struct http_connection *connection,
320 const struct http_request *request,
321 const struct arguments_otp_authentication *arguments) {
322 if (arguments == NULL) {
323 http_send_response_500(connection,
324 "Third argument of do_as_phase_two() is NULL");
325 return;
328 if (request->method != HTTP_METHOD_POST) {
329 http_send_response_400(connection,
330 "Second phase OTP request must be POST");
331 return;
334 if (!http_client_authenticates(request)) {
335 http_send_response_401_otp(connection,
336 auth_otp_method2string(arguments->method),
337 "authentication.error.userIsNotAuthenticated",
338 "Client did not send any authentication header");
339 return;
342 switch(http_authenticate_otp(request,
343 arguments->username, arguments->password, arguments->otp)) {
344 case HTTP_ERROR_SUCCESS: {
345 /* Find final URI */
346 char *uri = strstr(request->uri, "&uri=");
347 if (uri == NULL) {
348 http_send_response_400(connection,
349 "Missing uri parameter in Request URI");
350 return;
352 uri += 5;
353 /* Build new location for final request */
354 char *location = NULL;
355 if (NULL == (location = strdup(uri))) {
356 http_send_response_500(connection,
357 "Could not build new location for final request");
358 return;
360 char *terminator = strchr(location, '&');
361 if (NULL != terminator)
362 *terminator = '\0';
363 /* Generate pseudo-random cookie value. This is to prevent
364 * client from reusing the cookie accidentally. We use the
365 * same seed to get reproducible tests. */
366 if (-1 == test_asprintf(&authorization_cookie_value, "%d",
367 rand())) {
368 http_send_response_500(connection,
369 "Could not generate cookie value");
370 free(location);
371 return;
373 /* XXX: Add Path parameter to cookie, otherwise
374 * different paths will not match.
375 * FIXME: Domain argument does not work with cURL. Report a bug. */
376 http_send_response_302_cookie(connection,
377 authorization_cookie_name,
378 authorization_cookie_value,
379 /*http_find_host(request)*/NULL,
380 /*NULL*/"/",
381 location);
382 free(location);
384 break;
385 case HTTP_ERROR_CLIENT:
386 if (arguments->isds_deviations)
387 http_send_response_401_otp(connection,
388 auth_otp_method2string(arguments->method),
389 "authentication.error.userIsNotAuthenticated",
390 " Retry: Bad user name or password in second OTP phase.\r\n"
391 " This is very long header\r\n"
392 " which should span to more lines.\r\n"
393 " Surrounding LWS are meaning-less. ");
394 else
395 http_send_response_403(connection);
396 break;
397 default:
398 http_send_response_500(connection,
399 "Could not verify OTP authentication");
404 /* Process ASWS for changing OTP password requests */
405 /* FIXME: The ASWS URI hosts two services: for sending TOTP code for password
406 * change and for changing OTP password. The problem is the former one is
407 * basic-authenticated, the later one is otp-authenticated. But we cannot
408 * decide which authentication to enforce without understadning request body.
409 * I will just try both of them to choose the service.
410 * But I hope official server implementation does it in more clever way. */
411 static void do_asws(const struct http_connection *connection,
412 const struct http_request *request,
413 const struct arguments_otp_authentication *arguments) {
414 http_error error;
415 if (arguments == NULL) {
416 http_send_response_500(connection,
417 "Third argument of do_asws() is NULL");
418 return;
421 if (request->method != HTTP_METHOD_POST) {
422 http_send_response_400(connection, "ASWS request must be POST");
423 return;
426 if (!http_client_authenticates(request)) {
427 http_send_response_401_otp(connection,
428 auth_otp_method2string(arguments->method),
429 "authentication.error.userIsNotAuthenticated",
430 "Client did not send any authentication header");
431 return;
434 /* Try OTP */
435 error = http_authenticate_otp(request,
436 arguments->username, arguments->password, arguments->otp);
437 if (HTTP_ERROR_SUCCESS == error) {
438 /* This will be request for password change because OTP
439 * authentication succeeded. */
440 do_ws(connection, arguments->services, request, NULL);
441 return;
444 if (AUTH_OTP_TIME == arguments->method) {
445 /* Try Basic */
446 error = http_authenticate_basic(request,
447 arguments->username, arguments->password);
448 if (HTTP_ERROR_SUCCESS == error) {
449 /* This will be request for time code for password change because
450 * basic authentication succeeded. */
451 do_ws(connection, arguments->services, request, NULL);
452 return;
456 if (HTTP_ERROR_CLIENT == error) {
457 if (arguments->isds_deviations)
458 http_send_response_401_otp(connection,
459 auth_otp_method2string(arguments->method),
460 "authentication.error.userIsNotAuthenticated",
461 " Retry: Bad user name or password in Authorization.\r\n"
462 " This is very long header\r\n"
463 " which should span to more lines.\r\n"
464 " Surrounding LWS are meaning-less. ");
465 else
466 http_send_response_403(connection);
467 return;
470 http_send_response_500(connection,
471 "Could not verify OTP authentication");
475 /* Process OTP session cookie invalidation request */
476 static void do_as_logout(const struct http_connection *connection,
477 const struct http_request *request,
478 const struct arguments_otp_authentication *arguments) {
479 if (arguments == NULL) {
480 http_send_response_500(connection,
481 "Third argument of do_as_logout() is NULL");
482 return;
485 if (request->method != HTTP_METHOD_GET) {
486 http_send_response_400(connection,
487 "OTP cookie invalidation request must be GET");
488 return;
491 const char *received_cookie =
492 http_find_cookie(request, authorization_cookie_name);
494 if (authorization_cookie_value == NULL || received_cookie == NULL ||
495 strcmp(authorization_cookie_value, received_cookie)) {
496 http_send_response_403(connection);
497 return;
500 /* XXX: Add Path parameter to cookie, otherwise
501 * different paths will not match.
502 * FIXME: Domain argument does not work with cURL. Report a bug. */
503 http_send_response_200_cookie(connection,
504 authorization_cookie_name,
506 /*http_find_host(request)*/ NULL,
507 /*NULL*/"/",
508 NULL, 0, NULL);
512 /* Process ISDS WS ping authorized by cookie */
513 static void do_ws_with_cookie(const struct http_connection *connection,
514 const struct http_request *request,
515 const struct arguments_otp_authentication *arguments,
516 const char *valid_base_path) {
517 const char *received_cookie =
518 http_find_cookie(request, authorization_cookie_name);
520 if (authorization_cookie_value != NULL && received_cookie != NULL &&
521 !strcmp(authorization_cookie_value, received_cookie))
522 do_ws(connection, arguments->services, request, valid_base_path);
523 else
524 http_send_response_403(connection);
528 /* Do the server protocol with OTP authentication.
529 * @connection is HTTP connection
530 * @server_arguments is pointer to structure arguments_otp_authentication. It
531 * selects OTP method to enable.
532 * @request is parsed HTTP client requrest
533 * @return 0 to accept new client, return -1 in case of fatal error. */
534 int server_otp_authentication(const struct http_connection *connection,
535 const void *server_arguments, const struct http_request *request) {
536 const struct arguments_otp_authentication *arguments =
537 (const struct arguments_otp_authentication *) server_arguments;
539 if (NULL == arguments || NULL == request) {
540 return(-1);
543 if (arguments->username != NULL) {
544 if (arguments->method == AUTH_OTP_HMAC &&
545 !strncmp(request->uri, as_path_hotp, strlen(as_path_hotp))) {
546 do_as_phase_two(connection, request, arguments);
547 } else if (arguments->method == AUTH_OTP_TIME &&
548 !strncmp(request->uri, as_path_sendsms,
549 strlen(as_path_sendsms))) {
550 do_as_sendsms(connection, request, arguments);
551 } else if (arguments->method == AUTH_OTP_TIME &&
552 !strncmp(request->uri, as_path_dontsendsms,
553 strlen(as_path_dontsendsms))) {
554 do_as_phase_two(connection, request, arguments);
555 } else if (!strncmp(request->uri, as_path_logout,
556 strlen(as_path_logout))) {
557 do_as_logout(connection, request, arguments);
558 } else if (!strcmp(request->uri, asws_path)) {
559 do_asws(connection, request, arguments);
560 } else if (!strcmp(request->uri, ws_path)) {
561 do_ws_with_cookie(connection, request, arguments,
562 ws_base_path_otp);
563 } else {
564 http_send_response_400(connection,
565 "Unknown path for OTP authenticating service");
567 } else {
568 if (!strcmp(request->uri, ws_path)) {
569 do_ws(connection, arguments->services, request,
570 ws_base_path_otp);
571 } else {
572 http_send_response_400(connection,
573 "Unknown path for OTP authenticating service");
577 return 0;
581 /* Implementation of server that is out of order.
582 * It always sends back SOAP Fault with HTTP error 503.
583 * @connection is HTTP connection
584 * @server_arguments is ununsed pointer
585 * @request is parsed HTTP client request
586 * @return 0 to accept new client, return -1 in case of fatal error. */
587 int server_out_of_order(const struct http_connection *connection,
588 const void *server_arguments, const struct http_request *request) {
589 const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
590 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>";
592 http_send_response_503(connection, fault, strlen(fault),
593 soap_mime_type);
594 return 0;
598 /* Call-back for HTTP to receive data from socket
599 * API is equivalent to recv(2) except automatic interrupt handling. */
600 static ssize_t recv_plain(const struct http_connection *connection,
601 void *buffer, size_t length) {
602 ssize_t retval;
603 do {
604 retval = recv(connection->socket, buffer, length, 0);
605 } while (-1 == retval && EINTR == errno);
606 return retval;
610 /* Call-back for HTTP to sending data to socket
611 * API is equivalent to send(2) except automatic interrupt handling. */
612 static ssize_t send_plain(const struct http_connection *connection,
613 const void *buffer, size_t length) {
614 ssize_t retval;
615 do {
616 retval = send(connection->socket, buffer, length, MSG_NOSIGNAL);
617 } while (-1 == retval && EINTR == errno);
618 return retval;
622 /* Call-back for HTTP to receive data from TLS socket
623 * API is equivalent to recv(2) except automatic interrupt handling. */
624 static ssize_t recv_tls(const struct http_connection *connection,
625 void *buffer, size_t length) {
626 ssize_t retval;
627 do {
628 retval = gnutls_record_recv(connection->callback_data, buffer, length);
629 if (GNUTLS_E_REHANDSHAKE == retval) {
630 int error;
631 do {
632 error = gnutls_handshake(connection->callback_data);
633 } while (error < 0 && !gnutls_error_is_fatal(error));
634 if (error < 0) {
635 fprintf(stderr, "TLS rehandshake failed: %s\n",
636 gnutls_strerror(error));
637 return -1;
640 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
641 return (retval < 0) ? -1 : retval;
645 /* Call-back for HTTP to sending data to TLS socket
646 * API is equivalent to send(2) except automatic interrupt handling. */
647 static ssize_t send_tls(const struct http_connection *connection,
648 const void *buffer, size_t length) {
649 ssize_t retval;
650 do {
651 retval = gnutls_record_send(connection->callback_data, buffer, length);
652 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
653 return (retval < 0) ? -1 : retval;
657 /* Call-back fot GnuTLS to receive data from TCP socket.
658 * We override default implementation to unify passing http_connection as
659 * @context. Also otherwise there is need for ugly type cast from integer to
660 * pointer. */
661 static ssize_t tls_pull(gnutls_transport_ptr_t context, void* buffer,
662 size_t length) {
663 return recv( ((struct http_connection*)context)->socket,
664 buffer, length, 0);
668 /* Call-back fot GnuTLS to send data to TCP socket.
669 * GnuTLS does not call send(2) with MSG_NOSIGNAL, we must do it manually. */
670 static ssize_t tls_push(gnutls_transport_ptr_t context, const void* buffer,
671 size_t length) {
672 return send( ((struct http_connection*)context)->socket,
673 buffer, length, MSG_NOSIGNAL);
677 /* Verify client certificate from current TLS session.
678 * @tls_session is session in TLS handshake when client sent certificate
679 * @return 0 for acceptance, return non-0 for denial. */
680 static int tls_verify_client(gnutls_session_t tls_session) {
681 const gnutls_datum_t *chain; /* Pointer to static data */
682 unsigned int chain_length;
683 gnutls_x509_crt_t certificate;
684 gnutls_datum_t certificate_text;
685 unsigned int status;
686 char *dn_text;
687 size_t dn_size;
688 int error;
690 /* Obtain client's certificate chain */
691 chain = gnutls_certificate_get_peers(tls_session, &chain_length);
692 if (NULL == chain) {
693 fprintf(stderr, "Error while obtaining client's certificate\n");
694 return -1;
696 if (chain_length < 1) {
697 fprintf(stderr, "Client did not send any certificate\n");
698 return -1;
701 /* Print client's certificate */
702 error = gnutls_x509_crt_init(&certificate);
703 if (error) {
704 fprintf(stderr, "Could not initialize certificate storage: %s\n",
705 gnutls_strerror(error));
706 return -1;
708 error = gnutls_x509_crt_import(certificate, chain,
709 GNUTLS_X509_FMT_DER);
710 if (error) {
711 fprintf(stderr, "Could not parse client's X.509 certificate: %s\n",
712 gnutls_strerror(error));
713 gnutls_x509_crt_deinit(certificate);
714 return -1;
716 error = gnutls_x509_crt_print(certificate, GNUTLS_CRT_PRINT_ONELINE,
717 &certificate_text);
718 if (error) {
719 fprintf(stderr, "Could not print client's certificate: %s\n",
720 gnutls_strerror(error));
721 gnutls_x509_crt_deinit(certificate);
722 return -1;
724 fprintf(stderr, "Client sent certificate: %s\n", certificate_text.data);
725 gnutls_free(certificate_text.data);
727 /* Verify certificate signature and path */
728 error = gnutls_certificate_verify_peers2(tls_session, &status);
729 if (error) {
730 fprintf(stderr, "Could not verify client's certificate: %s\n",
731 gnutls_strerror(error));
732 gnutls_x509_crt_deinit(certificate);
733 return -1;
735 if (status) {
736 fprintf(stderr, "Client's certificate is not valid.\n");
737 gnutls_x509_crt_deinit(certificate);
738 return -1;
739 } else {
740 fprintf(stderr, "Client's certificate is valid.\n");
743 /* Authorize client */
744 error = gnutls_x509_crt_get_dn(certificate, NULL, &dn_size);
745 if (error != GNUTLS_E_SHORT_MEMORY_BUFFER) {
746 fprintf(stderr, "Could not determine client's "
747 "distinguished name size: %s.\n",
748 gnutls_strerror(error));
749 gnutls_x509_crt_deinit(certificate);
750 return -1;
752 dn_text = gnutls_malloc(dn_size);
753 if (NULL == dn_text) {
754 fprintf(stderr, "Could not allocate memory for client's "
755 "distinguished name.\n");
756 gnutls_x509_crt_deinit(certificate);
757 return -1;
759 error = gnutls_x509_crt_get_dn(certificate, dn_text, &dn_size);
760 if (error) {
761 fprintf(stderr, "Could obtain client's "
762 "distinguished name size: %s.\n",
763 gnutls_strerror(error));
764 gnutls_free(dn_text);
765 gnutls_x509_crt_deinit(certificate);
766 return -1;
768 gnutls_x509_crt_deinit(certificate);
769 if (client_required_dn != NULL &&
770 strcmp(client_required_dn, dn_text)) {
771 fprintf(stderr, "Client is not authorized: "
772 "Client's distinguished name `%s' does not match "
773 "required name `%s'.\n",
774 dn_text, client_required_dn);
775 gnutls_free(dn_text);
776 return -1;
778 fprintf(stderr, "Client is authorized.\n");
779 gnutls_free(dn_text);
780 return 0;
784 /* Start sever in separate process.
785 * @server_process is PID of forked server
786 * @server_address is automatically allocated TCP address of listening server
787 * @server_implementation points to kind of server to implement. Valid values
788 * are addresses of server_basic_authentication(),
789 * server_otp_authentication(), or server_out_of_order().
790 * @server_arguments is pointer to argument pass to @server_implementation. It
791 * usually contains:
792 * @username sets required user name server has to require. Set NULL to
793 * disable HTTP authentication.
794 * @password sets required password server has to require
795 * socket.
796 * @isds_deviations is flag to set conformance level. If false, server is
797 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
798 * specification. Otherwise server mimics real ISDS implementation as much
799 * as possible.
800 * @tls sets TLS layer. Pass NULL for plain HTTP.
801 * @return -1 in case of error. */
802 int start_server(pid_t *server_process, char **server_address,
803 int (*server_implementation)(const struct http_connection *,
804 const void *, const struct http_request *),
805 const void *server_arguments, const struct tls_authentication *tls) {
806 int server_socket;
807 int error;
809 if (server_address == NULL) {
810 set_server_error("start_server(): Got invalid server_address pointer");
811 return -1;
813 *server_address = NULL;
815 if (server_process == NULL) {
816 set_server_error("start_server(): Got invalid server_process pointer");
817 return -1;
820 if (NULL != tls && NULL == tls->server_certificate) {
821 /* XXX: X.509 TLS server requires server certificate. */
822 tls = NULL;
825 server_socket = listen_on_socket();
826 if (server_socket == -1) {
827 set_server_error("Could not create listening socket");
828 return -1;
831 *server_address = socket2url(server_socket, NULL != tls);
832 if (*server_address == NULL) {
833 close(server_socket);
834 set_server_error("Could not format address of listening socket");
835 return -1;
838 if (NULL != tls) {
839 const char *error_position;
840 if ((error = gnutls_global_init())) {
841 close(server_socket);
842 set_server_error("Could not initialize GnuTLS: %s",
843 gnutls_strerror(error));
844 return -1;
846 if ((error =
847 gnutls_certificate_allocate_credentials(&x509_credentials))) {
848 close(server_socket);
849 gnutls_global_deinit();
850 set_server_error("Could not allocate X.509 credentials: %s",
851 gnutls_strerror(error));
852 return -1;
854 if (NULL != tls->authority_certificate) {
855 if (0 > (error = gnutls_certificate_set_x509_trust_file(
856 x509_credentials, tls->authority_certificate,
857 GNUTLS_X509_FMT_PEM))) {
858 close(server_socket);
859 gnutls_certificate_free_credentials(x509_credentials);
860 gnutls_global_deinit();
861 set_server_error("Could not load authority certificate `%s': %s",
862 tls->authority_certificate, gnutls_strerror(error));
863 return -1;
866 if ((error = gnutls_certificate_set_x509_key_file(x509_credentials,
867 tls->server_certificate, tls->server_key,
868 GNUTLS_X509_FMT_PEM))) {
869 close(server_socket);
870 gnutls_certificate_free_credentials(x509_credentials);
871 gnutls_global_deinit();
872 set_server_error("Could not load server certificate or "
873 "private key `%s': %s", tls->server_certificate,
874 gnutls_strerror(error));
875 return -1;
877 if ((error = gnutls_priority_init(&priority_cache,
878 "PERFORMANCE", &error_position))) {
879 close(server_socket);
880 gnutls_certificate_free_credentials(x509_credentials);
881 gnutls_global_deinit();
882 if (error == GNUTLS_E_INVALID_REQUEST) {
883 set_server_error("Could not set TLS algorithm preferences: "
884 "%s Error at `%s'.",
885 gnutls_strerror(error), error_position);
886 set_server_error("Could not set TLS algorithm preferences: %s",
887 gnutls_strerror(error));
889 return -1;
891 /* XXX: priority_cache is linked from x509_credentials now.
892 * Deinitialization must free x509_credentials before priority_cache. */
894 if ((error = gnutls_dh_params_init(&dh_parameters))) {
895 close(server_socket);
896 gnutls_certificate_free_credentials(x509_credentials);
897 gnutls_priority_deinit(priority_cache);
898 gnutls_global_deinit();
899 set_server_error("Could not allocate Diffie-Hellman parameters: "
900 "%s", gnutls_strerror(error));
901 return -1;
903 if ((error = gnutls_dh_params_generate2(dh_parameters,
904 gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
905 GNUTLS_SEC_PARAM_LOW)))) {
906 close(server_socket);
907 gnutls_certificate_free_credentials(x509_credentials);
908 gnutls_priority_deinit(priority_cache);
909 gnutls_dh_params_deinit(dh_parameters);
910 gnutls_global_deinit();
911 set_server_error("Could not generate Diffie-Hellman parameters: %s",
912 gnutls_strerror(error));
913 return -1;
915 gnutls_certificate_set_dh_params(x509_credentials, dh_parameters);
916 /* XXX: dh_parameters are linked from x509_credentials now.
917 * Deinitialization must free x509_credentials before dh_parameters. */
920 *server_process = fork();
921 if (*server_process == -1) {
922 close(server_socket);
923 if (NULL != tls) {
924 gnutls_certificate_free_credentials(x509_credentials);
925 gnutls_priority_deinit(priority_cache);
926 gnutls_dh_params_deinit(dh_parameters);
927 gnutls_global_deinit();
929 set_server_error("Server could not been forked");
930 return -1;
933 if (*server_process == 0) {
934 int client_socket;
935 gnutls_session_t tls_session;
936 struct http_connection connection;
937 struct http_request *request = NULL;
938 http_error error;
939 int terminate = 0;
941 while (!terminate) {
942 if (NULL != tls) {
943 if ((error = gnutls_init(&tls_session, GNUTLS_SERVER))) {
944 set_server_error("Could not initialize TLS session: %s",
945 gnutls_strerror(error));
946 terminate = -1;
947 continue;
949 if ((error = gnutls_priority_set(tls_session,
950 priority_cache))) {
951 set_server_error(
952 "Could not set priorities to TLS session: %s",
953 gnutls_strerror(error));
954 terminate = -1;
955 gnutls_deinit(tls_session);
956 continue;
958 if ((error = gnutls_credentials_set(tls_session,
959 GNUTLS_CRD_CERTIFICATE, x509_credentials))) {
960 set_server_error("Could not set X509 credentials to TLS "
961 "session: %s", gnutls_strerror(error));
962 terminate = -1;
963 gnutls_deinit(tls_session);
964 continue;
966 /* XXX: Credentials are linked from session now.
967 * Deinitializition must free session before x509_credentials.
969 if (NULL != tls->client_name) {
970 client_required_dn = tls->client_name;
971 /* Require client certificate */
972 gnutls_certificate_server_set_request(tls_session,
973 GNUTLS_CERT_REQUIRE);
974 /* And verify it in TLS handshake */
975 gnutls_certificate_set_verify_function(x509_credentials,
976 tls_verify_client);
980 if (0 > (client_socket = accept(server_socket, NULL, NULL))) {
981 terminate = -1;
982 if (NULL != tls)
983 gnutls_deinit(tls_session);
984 continue;
986 fprintf(stderr, "Connection accepted\n");
987 connection.socket = client_socket;
989 if (NULL == tls) {
990 connection.recv_callback = recv_plain;
991 connection.send_callback = send_plain;
992 connection.callback_data = NULL;
993 } else {
994 connection.recv_callback = recv_tls;
995 connection.send_callback = send_tls;
996 connection.callback_data = tls_session;
997 gnutls_transport_set_pull_function(tls_session, tls_pull);
998 gnutls_transport_set_push_function(tls_session, tls_push);
999 gnutls_transport_set_ptr2(tls_session,
1000 &connection, &connection);
1001 do {
1002 error = gnutls_handshake(tls_session);
1003 } while (error < 0 && !gnutls_error_is_fatal(error));
1004 if (error < 0) {
1005 fprintf(stderr, "TLS handshake failed: %s\n",
1006 gnutls_strerror(error));
1007 close(client_socket);
1008 gnutls_deinit(tls_session);
1009 continue;
1013 error = http_read_request(&connection, &request);
1014 if (error) {
1015 fprintf(stderr, "Error while reading request\n");
1016 if (error == HTTP_ERROR_CLIENT)
1017 http_send_response_400(&connection, "Error in request");
1018 else
1019 http_send_response_500(&connection,
1020 "Could not read request");
1021 close(client_socket);
1022 if (NULL != tls)
1023 gnutls_deinit(tls_session);
1024 continue;
1027 terminate = server_implementation(&connection, server_arguments,
1028 request);
1030 http_request_free(&request);
1031 close(client_socket);
1032 if (NULL != tls) {
1033 gnutls_deinit(tls_session);
1037 close(server_socket);
1038 free(authorization_cookie_value);
1039 exit(EXIT_SUCCESS);
1040 /* Does not return */
1043 return 0;
1047 /* Kill the server process.
1048 * Return 0. Return -1 if server could not been stopped. Return 1 if server
1049 * crashed. */
1050 int stop_server(pid_t server_process) {
1051 int status;
1052 if (server_process <= 0) {
1053 set_server_error("Invalid server PID to kill");
1054 return -1;
1056 if (-1 == kill(server_process, SIGTERM)) {
1057 set_server_error("Could not terminate server");
1058 return -1;
1060 if (-1 == waitpid(server_process, &status, 0)) {
1061 set_server_error("Could not wait for server termination");
1062 return -1;
1064 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
1065 set_server_error("Server terminated by signal %d violently",
1066 WTERMSIG(status));
1067 return 1;
1069 return 0;