l10n: Update translation catalogues
[libisds.git] / test / simline / server.c
blobc5412ea369730656cdab93ee6ab305844abf2469
1 #ifndef _POSIX_SOURCE
2 #define _POSIX_SOURCE /* For getaddrinfo(3) */
3 #endif
5 #ifndef _BSD_SOURCE
6 #define _BSD_SOURCE /* For NI_MAXHOST up to glibc-2.19 */
7 #endif
8 #ifndef _DEFAULT_SOURCE
9 #define _DEFAULT_SOURCE /* For NI_MAXHOST since glibc-2.20 */
10 #endif
12 #include "../test-tools.h"
13 #include "http.h"
14 #include "server.h"
15 #include "service.h"
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <errno.h> /* For EINTR */
22 #include <netdb.h>
23 #include <string.h>
24 #include <signal.h>
25 #include <unistd.h>
26 #include <wait.h>
27 #include <gnutls/gnutls.h>
28 #include <gnutls/x509.h>
30 char *server_error = NULL;
32 static const char *as_path_hotp = "/as/processLogin?type=hotp&uri=";
33 static const char *as_path_sendsms = "/as/processLogin?type=totp&sendSms=true&uri=";
34 static const char *as_path_dontsendsms = "/as/processLogin?type=totp&uri=";
35 static const char *as_path_logout = "/as/processLogout?uri=";
36 static const char *asws_path = "/asws/changePassword";
37 static const char *ws_path = "/apps/DS/dz";
39 static const char *ws_base_path_basic = "/";
40 static const char *ws_base_path_commercial_certificate_with_password = "/certds/";
41 static const char *ws_base_path_otp = "/apps/";
43 static const char *authorization_cookie_name = "IPCZ-X-COOKIE";
44 static char *authorization_cookie_value = NULL;
46 /* TLS stuff */
47 static gnutls_certificate_credentials_t x509_credentials;
48 static gnutls_priority_t priority_cache;
49 static gnutls_dh_params_t dh_parameters;
50 static gnutls_datum_t ticket_key;
51 static const char *client_required_dn = NULL;
53 /* Save error message if not yet set. The message will be duplicated.
54 * @message is printf(3) formatting string. */
55 void set_server_error(const char *message, ...) {
56 if (server_error == NULL) {
57 va_list ap;
58 va_start(ap, message);
59 test_vasprintf(&server_error, message, ap);
60 va_end(ap);
65 /* Creates listening TCP socket on localhost.
66 * Returns the socket descriptor or -1. */
67 int listen_on_socket(void) {
68 int retval;
69 struct addrinfo hints;
70 struct addrinfo *addresses, *address;
71 int fd;
73 memset(&hints, 0, sizeof(hints));
74 hints.ai_family = AF_UNSPEC;
75 hints.ai_socktype = SOCK_STREAM;
76 retval = getaddrinfo("localhost", NULL, &hints, &addresses);
77 if (retval) {
78 set_server_error("Could not resolve `localhost'");
79 return -1;
82 for (address = addresses; address != NULL; address = address->ai_next) {
83 fd = socket(address->ai_family, address->ai_socktype,
84 address->ai_protocol);
85 if (fd == -1) continue;
87 retval = bind(fd, address->ai_addr, address->ai_addrlen);
88 if (retval != 0) {
89 close(fd);
90 continue;
93 retval = listen(fd, 0);
94 if (retval == 0) {
95 freeaddrinfo(addresses);
96 return fd;
99 close(fd);
102 freeaddrinfo(addresses);
103 set_server_error("Could not start listening on TCP/localhost");
104 return -1;
108 /* Format socket address as printable string.
109 * @return allocated string or NULL in case of error. */
110 char *socket2address(int socket) {
111 struct sockaddr_storage storage;
112 socklen_t length = (socklen_t) sizeof(storage);
113 char host[NI_MAXHOST];
114 char service[NI_MAXSERV];
115 char *address = NULL;
117 if (-1 == getsockname(socket, (struct sockaddr *)&storage, &length)) {
118 set_server_error("Could not get address of server socket");
119 return NULL;
122 if (0 != getnameinfo((struct sockaddr *)&storage, length,
123 host, sizeof(host), service, sizeof(service),
124 NI_NUMERICHOST|NI_NUMERICSERV)) {
125 set_server_error("Could not resolve address of server socket");
126 return NULL;
129 if (-1 == test_asprintf(&address,
130 (strchr(host, ':') == NULL) ? "%s:%s" : "[%s]:%s",
131 host, service)) {
132 set_server_error("Could not format server address");
133 return NULL;
136 return address;
140 /* Format HTTP(s) socket address as printable URL string.
141 * @socket is listening TCP socket of HTTP server
142 * @tls is true for HTTPS, false for plain HTTP
143 * @return allocated string or NULL in case of error. */
144 static char *socket2url(int socket, _Bool tls) {
145 char *socket_address = NULL;
146 char *address = NULL;
148 socket_address = socket2address(socket);
149 if (NULL == socket_address) {
150 set_server_error("Could not format server address");
151 free(socket_address);
152 return NULL;
155 if (-1 == test_asprintf(&address, "%s://%s/",
156 (tls) ? "https" : "http", socket_address)) {
157 set_server_error("Could not format server address");
158 free(socket_address);
159 return NULL;
162 free(socket_address);
163 return address;
167 /* Process ISDS web service */
168 static void do_ws(const struct http_connection *connection,
169 const struct service_configuration *ws_configuration,
170 const struct http_request *request, const char *required_base_path) {
171 char *end_point = request->uri; /* Pointer to string in the request */
173 if (request->method != HTTP_METHOD_POST) {
174 http_send_response_400(connection,
175 "Regular ISDS web service request must be POST");
176 return;
179 if (required_base_path != NULL) {
180 size_t required_base_path_length = strlen(required_base_path);
181 if (strncmp(end_point, required_base_path, required_base_path_length)) {
182 http_send_response_400(connection, "Request sent to invalid path");
183 return;
185 end_point += required_base_path_length;
188 soap(connection, ws_configuration, request->body, request->body_length,
189 end_point);
193 /* Do the server protocol.
194 * @connection is HTTP connection
195 * @server_arguments is pointer to structure arguments_basic_authentication
196 * @request is parsed HTTP client request
197 * @prefix is HTTP URI path prefix (directory) where all ISDS services live
198 * @return 0 to accept new client, return -1 in case of fatal error. */
199 static int server_prefixed_basic_authentication(
200 const struct http_connection *connection, const void *server_arguments,
201 const struct http_request *request, const char *prefix) {
202 const struct arguments_basic_authentication *arguments =
203 (const struct arguments_basic_authentication *) server_arguments;
205 if (NULL == arguments || NULL == request || NULL == prefix) {
206 return -1;
209 if (request->method == HTTP_METHOD_POST) {
210 /* Only POST requests are used in Basic authentication mode */
211 if (arguments->username != NULL) {
212 if (http_client_authenticates(request)) {
213 switch(http_authenticate_basic(request,
214 arguments->username, arguments->password)) {
215 case HTTP_ERROR_SUCCESS:
216 do_ws(connection, arguments->services, request,
217 prefix);
218 break;
219 case HTTP_ERROR_CLIENT:
220 if (arguments->isds_deviations)
221 http_send_response_401_basic(connection);
222 else
223 http_send_response_403(connection);
224 break;
225 default:
226 http_send_response_500(connection,
227 "Server error while verifying Basic "
228 "authentication");
230 } else {
231 http_send_response_401_basic(connection);
233 } else {
234 do_ws(connection, arguments->services, request,
235 prefix);
237 } else {
238 /* HTTP method unsupported per ISDS specification */
239 http_send_response_400(connection,
240 "Only POST method is allowed");
243 return 0;
247 /* Do the server protocol.
248 * @connection is HTTP connection
249 * @server_arguments is pointer to structure arguments_basic_authentication
250 * @request is parsed HTTP client request
251 * @return 0 to accept new client, return -1 in case of fatal error. */
252 int server_basic_authentication(const struct http_connection *connection,
253 const void *server_arguments, const struct http_request *request) {
254 return server_prefixed_basic_authentication(connection, server_arguments,
255 request, ws_base_path_basic);
259 /* Do the server protocol.
260 * @connection is HTTP connection
261 * @server_arguments is pointer to structure arguments_basic_authentication
262 * @request is parsed HTTP client request
263 * @return 0 to accept new client, return -1 in case of fatal error. */
264 int server_certificate_with_password_authentication(
265 const struct http_connection *connection,
266 const void *server_arguments, const struct http_request *request) {
267 return server_prefixed_basic_authentication(connection, server_arguments,
268 request, ws_base_path_commercial_certificate_with_password);
272 /* Process first phase of TOTP request */
273 static void do_as_sendsms(const struct http_connection *connection,
274 const struct http_request *request,
275 const struct arguments_otp_authentication *arguments) {
276 if (arguments == NULL) {
277 http_send_response_500(connection,
278 "Third argument of do_as_sendsms() is NULL");
279 return;
282 if (request->method != HTTP_METHOD_POST) {
283 http_send_response_400(connection,
284 "First phase TOTP request must be POST");
285 return;
288 if (!http_client_authenticates(request)) {
289 http_send_response_401_otp(connection,
290 "totpsendsms",
291 "authentication.error.userIsNotAuthenticated",
292 "Client did not send any authentication header");
293 return;
296 switch(http_authenticate_basic(request,
297 arguments->username, arguments->password)) {
298 case HTTP_ERROR_SUCCESS: {
299 /* Find final URI */
300 char *uri = strstr(request->uri, "&uri=");
301 if (uri == NULL) {
302 http_send_response_400(connection,
303 "Missing uri parameter in Request URI");
304 return;
306 uri += 5;
307 /* Build new location for second OTP phase */
308 char *location = NULL;
309 if (-1 == test_asprintf(&location, "%s%s", as_path_dontsendsms, uri)) {
310 http_send_response_500(connection,
311 "Could not build new localtion for "
312 "second OTP phase");
313 return;
315 char *terminator = strchr(uri, '&');
316 if (NULL != terminator)
317 location[strlen(as_path_dontsendsms) + (uri - terminator)] = '\0';
318 http_send_response_302_totp(connection,
319 "authentication.info.totpSended",
320 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
321 location);
322 free(location);
324 break;
325 case HTTP_ERROR_CLIENT:
326 if (arguments->isds_deviations)
327 http_send_response_401_otp(connection,
328 "totpsendsms",
329 "authentication.error.userIsNotAuthenticated",
330 " Retry: Bad user name or password in first OTP phase.\r\n"
331 " This is very long header\r\n"
332 " which should span to more lines.\r\n"
333 " Surrounding LWS are meaning-less. ");
334 else
335 http_send_response_403(connection);
336 break;
337 default:
338 http_send_response_500(connection,
339 "Could not verify Basic authentication");
344 /* Return static string representation of HTTP OTP authentication method.
345 * Or NULL in case of error. */
346 static const char *auth_otp_method2string(enum auth_otp_method method) {
347 switch (method) {
348 case AUTH_OTP_HMAC: return "hotp";
349 case AUTH_OTP_TIME: return "totp";
350 default: return NULL;
355 /* Process second phase of OTP request */
356 static void do_as_phase_two(const struct http_connection *connection,
357 const struct http_request *request,
358 const struct arguments_otp_authentication *arguments) {
359 if (arguments == NULL) {
360 http_send_response_500(connection,
361 "Third argument of do_as_phase_two() is NULL");
362 return;
365 if (request->method != HTTP_METHOD_POST) {
366 http_send_response_400(connection,
367 "Second phase OTP request must be POST");
368 return;
371 if (!http_client_authenticates(request)) {
372 http_send_response_401_otp(connection,
373 auth_otp_method2string(arguments->method),
374 "authentication.error.userIsNotAuthenticated",
375 "Client did not send any authentication header");
376 return;
379 switch(http_authenticate_otp(request,
380 arguments->username, arguments->password, arguments->otp)) {
381 case HTTP_ERROR_SUCCESS: {
382 /* Find final URI */
383 char *uri = strstr(request->uri, "&uri=");
384 if (uri == NULL) {
385 http_send_response_400(connection,
386 "Missing uri parameter in Request URI");
387 return;
389 uri += 5;
390 /* Build new location for final request */
391 char *location = NULL;
392 if (NULL == (location = strdup(uri))) {
393 http_send_response_500(connection,
394 "Could not build new location for final request");
395 return;
397 char *terminator = strchr(location, '&');
398 if (NULL != terminator)
399 *terminator = '\0';
400 /* Generate pseudo-random cookie value. This is to prevent
401 * client from reusing the cookie accidentally. We use the
402 * same seed to get reproducible tests. */
403 if (-1 == test_asprintf(&authorization_cookie_value, "%d",
404 rand())) {
405 http_send_response_500(connection,
406 "Could not generate cookie value");
407 free(location);
408 return;
410 /* XXX: Add Path parameter to cookie, otherwise
411 * different paths will not match.
412 * FIXME: Domain argument does not work with cURL. Report a bug. */
413 http_send_response_302_cookie(connection,
414 authorization_cookie_name,
415 authorization_cookie_value,
416 /*http_find_host(request)*/NULL,
417 /*NULL*/"/",
418 location);
419 free(location);
421 break;
422 case HTTP_ERROR_CLIENT:
423 if (arguments->isds_deviations)
424 http_send_response_401_otp(connection,
425 auth_otp_method2string(arguments->method),
426 "authentication.error.userIsNotAuthenticated",
427 " Retry: Bad user name or password in second OTP phase.\r\n"
428 " This is very long header\r\n"
429 " which should span to more lines.\r\n"
430 " Surrounding LWS are meaning-less. ");
431 else
432 http_send_response_403(connection);
433 break;
434 default:
435 http_send_response_500(connection,
436 "Could not verify OTP authentication");
441 /* Process ASWS for changing OTP password requests */
442 /* FIXME: The ASWS URI hosts two services: for sending TOTP code for password
443 * change and for changing OTP password. The problem is the former one is
444 * basic-authenticated, the later one is otp-authenticated. But we cannot
445 * decide which authentication to enforce without understadning request body.
446 * I will just try both of them to choose the service.
447 * But I hope official server implementation does it in more clever way. */
448 static void do_asws(const struct http_connection *connection,
449 const struct http_request *request,
450 const struct arguments_otp_authentication *arguments) {
451 http_error error;
452 if (arguments == NULL) {
453 http_send_response_500(connection,
454 "Third argument of do_asws() is NULL");
455 return;
458 if (request->method != HTTP_METHOD_POST) {
459 http_send_response_400(connection, "ASWS request must be POST");
460 return;
463 if (!http_client_authenticates(request)) {
464 http_send_response_401_otp(connection,
465 auth_otp_method2string(arguments->method),
466 "authentication.error.userIsNotAuthenticated",
467 "Client did not send any authentication header");
468 return;
471 /* Try OTP */
472 error = http_authenticate_otp(request,
473 arguments->username, arguments->password, arguments->otp);
474 if (HTTP_ERROR_SUCCESS == error) {
475 /* This will be request for password change because OTP
476 * authentication succeeded. */
477 do_ws(connection, arguments->services, request, NULL);
478 return;
481 if (AUTH_OTP_TIME == arguments->method) {
482 /* Try Basic */
483 error = http_authenticate_basic(request,
484 arguments->username, arguments->password);
485 if (HTTP_ERROR_SUCCESS == error) {
486 /* This will be request for time code for password change because
487 * basic authentication succeeded. */
488 do_ws(connection, arguments->services, request, NULL);
489 return;
493 if (HTTP_ERROR_CLIENT == error) {
494 if (arguments->isds_deviations)
495 http_send_response_401_otp(connection,
496 auth_otp_method2string(arguments->method),
497 "authentication.error.userIsNotAuthenticated",
498 " Retry: Bad user name or password in Authorization.\r\n"
499 " This is very long header\r\n"
500 " which should span to more lines.\r\n"
501 " Surrounding LWS are meaning-less. ");
502 else
503 http_send_response_403(connection);
504 return;
507 http_send_response_500(connection,
508 "Could not verify OTP authentication");
512 /* Process OTP session cookie invalidation request */
513 static void do_as_logout(const struct http_connection *connection,
514 const struct http_request *request,
515 const struct arguments_otp_authentication *arguments) {
516 if (arguments == NULL) {
517 http_send_response_500(connection,
518 "Third argument of do_as_logout() is NULL");
519 return;
522 if (request->method != HTTP_METHOD_GET) {
523 http_send_response_400(connection,
524 "OTP cookie invalidation request must be GET");
525 return;
528 const char *received_cookie =
529 http_find_cookie(request, authorization_cookie_name);
531 if (authorization_cookie_value == NULL || received_cookie == NULL ||
532 strcmp(authorization_cookie_value, received_cookie)) {
533 http_send_response_403(connection);
534 return;
537 /* XXX: Add Path parameter to cookie, otherwise
538 * different paths will not match.
539 * FIXME: Domain argument does not work with cURL. Report a bug. */
540 http_send_response_200_cookie(connection,
541 authorization_cookie_name,
543 /*http_find_host(request)*/ NULL,
544 /*NULL*/"/",
545 NULL, 0, NULL);
549 /* Process ISDS WS ping authorized by cookie */
550 static void do_ws_with_cookie(const struct http_connection *connection,
551 const struct http_request *request,
552 const struct arguments_otp_authentication *arguments,
553 const char *valid_base_path) {
554 const char *received_cookie =
555 http_find_cookie(request, authorization_cookie_name);
557 if (authorization_cookie_value != NULL && received_cookie != NULL &&
558 !strcmp(authorization_cookie_value, received_cookie))
559 do_ws(connection, arguments->services, request, valid_base_path);
560 else
561 http_send_response_403(connection);
565 /* Do the server protocol with OTP authentication.
566 * @connection is HTTP connection
567 * @server_arguments is pointer to structure arguments_otp_authentication. It
568 * selects OTP method to enable.
569 * @request is parsed HTTP client requrest
570 * @return 0 to accept new client, return -1 in case of fatal error. */
571 int server_otp_authentication(const struct http_connection *connection,
572 const void *server_arguments, const struct http_request *request) {
573 const struct arguments_otp_authentication *arguments =
574 (const struct arguments_otp_authentication *) server_arguments;
576 if (NULL == arguments || NULL == request) {
577 return(-1);
580 if (arguments->username != NULL) {
581 if (arguments->method == AUTH_OTP_HMAC &&
582 !strncmp(request->uri, as_path_hotp, strlen(as_path_hotp))) {
583 do_as_phase_two(connection, request, arguments);
584 } else if (arguments->method == AUTH_OTP_TIME &&
585 !strncmp(request->uri, as_path_sendsms,
586 strlen(as_path_sendsms))) {
587 do_as_sendsms(connection, request, arguments);
588 } else if (arguments->method == AUTH_OTP_TIME &&
589 !strncmp(request->uri, as_path_dontsendsms,
590 strlen(as_path_dontsendsms))) {
591 do_as_phase_two(connection, request, arguments);
592 } else if (!strncmp(request->uri, as_path_logout,
593 strlen(as_path_logout))) {
594 do_as_logout(connection, request, arguments);
595 } else if (!strcmp(request->uri, asws_path)) {
596 do_asws(connection, request, arguments);
597 } else if (!strcmp(request->uri, ws_path)) {
598 do_ws_with_cookie(connection, request, arguments,
599 ws_base_path_otp);
600 } else {
601 http_send_response_400(connection,
602 "Unknown path for OTP authenticating service");
604 } else {
605 if (!strcmp(request->uri, ws_path)) {
606 do_ws(connection, arguments->services, request,
607 ws_base_path_otp);
608 } else {
609 http_send_response_400(connection,
610 "Unknown path for OTP authenticating service");
614 return 0;
618 /* Implementation of server that is out of order.
619 * It always sends back SOAP Fault with HTTP error 503.
620 * @connection is HTTP connection
621 * @server_arguments is ununsed pointer
622 * @request is parsed HTTP client request
623 * @return 0 to accept new client, return -1 in case of fatal error. */
624 int server_out_of_order(const struct http_connection *connection,
625 const void *server_arguments, const struct http_request *request) {
626 const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
627 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>";
628 (void)server_arguments;
629 (void)request;
631 http_send_response_503(connection, fault, strlen(fault),
632 soap_mime_type);
633 return 0;
637 /* Call-back for HTTP to receive data from socket
638 * API is equivalent to recv(2) except automatic interrupt handling. */
639 static ssize_t recv_plain(const struct http_connection *connection,
640 void *buffer, size_t length) {
641 ssize_t retval;
642 do {
643 retval = recv(connection->socket, buffer, length, 0);
644 } while (-1 == retval && EINTR == errno);
645 return retval;
649 /* Call-back for HTTP to sending data to socket
650 * API is equivalent to send(2) except automatic interrupt handling. */
651 static ssize_t send_plain(const struct http_connection *connection,
652 const void *buffer, size_t length) {
653 ssize_t retval;
654 do {
655 retval = send(connection->socket, buffer, length, MSG_NOSIGNAL);
656 } while (-1 == retval && EINTR == errno);
657 return retval;
661 /* Call-back for HTTP to receive data from TLS socket
662 * API is equivalent to recv(2) except automatic interrupt handling. */
663 static ssize_t recv_tls(const struct http_connection *connection,
664 void *buffer, size_t length) {
665 ssize_t retval;
666 do {
667 retval = gnutls_record_recv(connection->callback_data, buffer, length);
668 if (GNUTLS_E_REHANDSHAKE == retval) {
669 int error;
670 do {
671 error = gnutls_handshake(connection->callback_data);
672 } while (error < 0 && !gnutls_error_is_fatal(error));
673 if (error < 0) {
674 fprintf(stderr, "TLS rehandshake failed: %s\n",
675 gnutls_strerror(error));
676 return -1;
679 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
680 return (retval < 0) ? -1 : retval;
684 /* Call-back for HTTP to sending data to TLS socket
685 * API is equivalent to send(2) except automatic interrupt handling. */
686 static ssize_t send_tls(const struct http_connection *connection,
687 const void *buffer, size_t length) {
688 ssize_t retval;
689 do {
690 retval = gnutls_record_send(connection->callback_data, buffer, length);
691 } while (GNUTLS_E_INTERRUPTED == retval || GNUTLS_E_AGAIN == retval);
692 return (retval < 0) ? -1 : retval;
696 /* Call-back fot GnuTLS to receive data from TCP socket.
697 * We override default implementation to unify passing http_connection as
698 * @context. Also otherwise there is need for ugly type cast from integer to
699 * pointer. */
700 static ssize_t tls_pull(gnutls_transport_ptr_t context, void* buffer,
701 size_t length) {
702 return recv( ((struct http_connection*)context)->socket,
703 buffer, length, 0);
707 /* Call-back fot GnuTLS to send data to TCP socket.
708 * GnuTLS does not call send(2) with MSG_NOSIGNAL, we must do it manually. */
709 static ssize_t tls_push(gnutls_transport_ptr_t context, const void* buffer,
710 size_t length) {
711 return send( ((struct http_connection*)context)->socket,
712 buffer, length, MSG_NOSIGNAL);
716 /* Verify client certificate from current TLS session.
717 * @tls_session is session in TLS handshake when client sent certificate
718 * @return 0 for acceptance, return non-0 for denial. */
719 static int tls_verify_client(gnutls_session_t tls_session) {
720 const gnutls_datum_t *chain; /* Pointer to static data */
721 unsigned int chain_length;
722 gnutls_x509_crt_t certificate;
723 gnutls_datum_t certificate_text;
724 unsigned int status;
725 char *dn_text;
726 size_t dn_size;
727 int error;
729 /* Obtain client's certificate chain */
730 chain = gnutls_certificate_get_peers(tls_session, &chain_length);
731 if (NULL == chain) {
732 fprintf(stderr, "Error while obtaining client's certificate\n");
733 return -1;
735 if (chain_length < 1) {
736 fprintf(stderr, "Client did not send any certificate\n");
737 return -1;
740 /* Print client's certificate */
741 error = gnutls_x509_crt_init(&certificate);
742 if (error) {
743 fprintf(stderr, "Could not initialize certificate storage: %s\n",
744 gnutls_strerror(error));
745 return -1;
747 error = gnutls_x509_crt_import(certificate, chain,
748 GNUTLS_X509_FMT_DER);
749 if (error) {
750 fprintf(stderr, "Could not parse client's X.509 certificate: %s\n",
751 gnutls_strerror(error));
752 gnutls_x509_crt_deinit(certificate);
753 return -1;
755 error = gnutls_x509_crt_print(certificate, GNUTLS_CRT_PRINT_ONELINE,
756 &certificate_text);
757 if (error) {
758 fprintf(stderr, "Could not print client's certificate: %s\n",
759 gnutls_strerror(error));
760 gnutls_x509_crt_deinit(certificate);
761 return -1;
763 fprintf(stderr, "Client sent certificate: %s\n", certificate_text.data);
764 gnutls_free(certificate_text.data);
766 /* Verify certificate signature and path */
767 error = gnutls_certificate_verify_peers2(tls_session, &status);
768 if (error) {
769 fprintf(stderr, "Could not verify client's certificate: %s\n",
770 gnutls_strerror(error));
771 gnutls_x509_crt_deinit(certificate);
772 return -1;
774 if (status) {
775 fprintf(stderr, "Client's certificate is not valid.\n");
776 gnutls_x509_crt_deinit(certificate);
777 return -1;
778 } else {
779 fprintf(stderr, "Client's certificate is valid.\n");
782 /* Authorize client */
783 error = gnutls_x509_crt_get_dn(certificate, NULL, &dn_size);
784 if (error != GNUTLS_E_SHORT_MEMORY_BUFFER) {
785 fprintf(stderr, "Could not determine client's "
786 "distinguished name size: %s.\n",
787 gnutls_strerror(error));
788 gnutls_x509_crt_deinit(certificate);
789 return -1;
791 dn_text = gnutls_malloc(dn_size);
792 if (NULL == dn_text) {
793 fprintf(stderr, "Could not allocate memory for client's "
794 "distinguished name.\n");
795 gnutls_x509_crt_deinit(certificate);
796 return -1;
798 error = gnutls_x509_crt_get_dn(certificate, dn_text, &dn_size);
799 if (error) {
800 fprintf(stderr, "Could obtain client's "
801 "distinguished name size: %s.\n",
802 gnutls_strerror(error));
803 gnutls_free(dn_text);
804 gnutls_x509_crt_deinit(certificate);
805 return -1;
807 gnutls_x509_crt_deinit(certificate);
808 if (client_required_dn != NULL &&
809 strcmp(client_required_dn, dn_text)) {
810 fprintf(stderr, "Client is not authorized: "
811 "Client's distinguished name `%s' does not match "
812 "required name `%s'.\n",
813 dn_text, client_required_dn);
814 gnutls_free(dn_text);
815 return -1;
817 fprintf(stderr, "Client is authorized.\n");
818 gnutls_free(dn_text);
819 return 0;
823 /* Start sever in separate process.
824 * @server_process is PID of forked server
825 * @server_address is automatically allocated TCP address of listening server
826 * @server_implementation points to kind of server to implement. Valid values
827 * are addresses of server_basic_authentication(),
828 * server_otp_authentication(), or server_out_of_order().
829 * @server_arguments is pointer to argument pass to @server_implementation. It
830 * usually contains:
831 * @username sets required user name server has to require. Set NULL to
832 * disable HTTP authentication.
833 * @password sets required password server has to require
834 * socket.
835 * @isds_deviations is flag to set conformance level. If false, server is
836 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
837 * specification. Otherwise server mimics real ISDS implementation as much
838 * as possible.
839 * @tls sets TLS layer. Pass NULL for plain HTTP.
840 * @return -1 in case of error. */
841 int start_server(pid_t *server_process, char **server_address,
842 int (*server_implementation)(const struct http_connection *,
843 const void *, const struct http_request *),
844 const void *server_arguments, const struct tls_authentication *tls) {
845 int server_socket;
846 int error;
848 if (server_address == NULL) {
849 set_server_error("start_server(): Got invalid server_address pointer");
850 return -1;
852 *server_address = NULL;
854 if (server_process == NULL) {
855 set_server_error("start_server(): Got invalid server_process pointer");
856 return -1;
859 if (NULL != tls && NULL == tls->server_certificate) {
860 /* XXX: X.509 TLS server requires server certificate. */
861 tls = NULL;
864 server_socket = listen_on_socket();
865 if (server_socket == -1) {
866 set_server_error("Could not create listening socket");
867 return -1;
870 *server_address = socket2url(server_socket, NULL != tls);
871 if (*server_address == NULL) {
872 close(server_socket);
873 set_server_error("Could not format address of listening socket");
874 return -1;
877 if (NULL != tls) {
878 const char *error_position;
879 if ((error = gnutls_global_init())) {
880 close(server_socket);
881 set_server_error("Could not initialize GnuTLS: %s",
882 gnutls_strerror(error));
883 return -1;
885 if ((error =
886 gnutls_certificate_allocate_credentials(&x509_credentials))) {
887 close(server_socket);
888 gnutls_global_deinit();
889 set_server_error("Could not allocate X.509 credentials: %s",
890 gnutls_strerror(error));
891 return -1;
893 if (NULL != tls->authority_certificate) {
894 if (0 > (error = gnutls_certificate_set_x509_trust_file(
895 x509_credentials, tls->authority_certificate,
896 GNUTLS_X509_FMT_PEM))) {
897 close(server_socket);
898 gnutls_certificate_free_credentials(x509_credentials);
899 gnutls_global_deinit();
900 set_server_error("Could not load authority certificate `%s': %s",
901 tls->authority_certificate, gnutls_strerror(error));
902 return -1;
905 if ((error = gnutls_certificate_set_x509_key_file(x509_credentials,
906 tls->server_certificate, tls->server_key,
907 GNUTLS_X509_FMT_PEM))) {
908 close(server_socket);
909 gnutls_certificate_free_credentials(x509_credentials);
910 gnutls_global_deinit();
911 set_server_error("Could not load server certificate or "
912 "private key `%s': %s", tls->server_certificate,
913 gnutls_strerror(error));
914 return -1;
916 if ((error = gnutls_priority_init(&priority_cache,
917 "PERFORMANCE", &error_position))) {
918 close(server_socket);
919 gnutls_certificate_free_credentials(x509_credentials);
920 gnutls_global_deinit();
921 if (error == GNUTLS_E_INVALID_REQUEST) {
922 set_server_error("Could not set TLS algorithm preferences: "
923 "%s Error at `%s'.",
924 gnutls_strerror(error), error_position);
925 set_server_error("Could not set TLS algorithm preferences: %s",
926 gnutls_strerror(error));
928 return -1;
930 /* XXX: priority_cache is linked from x509_credentials now.
931 * Deinitialization must free x509_credentials before priority_cache. */
933 if ((error = gnutls_dh_params_init(&dh_parameters))) {
934 close(server_socket);
935 gnutls_certificate_free_credentials(x509_credentials);
936 gnutls_priority_deinit(priority_cache);
937 gnutls_global_deinit();
938 set_server_error("Could not allocate Diffie-Hellman parameters: "
939 "%s", gnutls_strerror(error));
940 return -1;
942 if ((error = gnutls_dh_params_generate2(dh_parameters,
943 gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
944 GNUTLS_SEC_PARAM_LOW)))) {
945 close(server_socket);
946 gnutls_certificate_free_credentials(x509_credentials);
947 gnutls_priority_deinit(priority_cache);
948 gnutls_dh_params_deinit(dh_parameters);
949 gnutls_global_deinit();
950 set_server_error("Could not generate Diffie-Hellman parameters: %s",
951 gnutls_strerror(error));
952 return -1;
954 gnutls_certificate_set_dh_params(x509_credentials, dh_parameters);
955 /* XXX: dh_parameters are linked from x509_credentials now.
956 * Deinitialization must free x509_credentials before dh_parameters. */
957 if ((error = gnutls_session_ticket_key_generate(&ticket_key))) {
958 fprintf(stderr, "Could not generate TLS session ticket key: %s\n",
959 gnutls_strerror(error));
960 ticket_key.data = NULL;
964 *server_process = fork();
965 if (*server_process == -1) {
966 close(server_socket);
967 if (NULL != tls) {
968 gnutls_free(ticket_key.data);
969 gnutls_certificate_free_credentials(x509_credentials);
970 gnutls_priority_deinit(priority_cache);
971 gnutls_dh_params_deinit(dh_parameters);
972 gnutls_global_deinit();
974 set_server_error("Server could not been forked");
975 return -1;
978 if (*server_process == 0) {
979 int client_socket;
980 gnutls_session_t tls_session;
981 struct http_connection connection;
982 struct http_request *request = NULL;
983 http_error error;
984 int terminate = 0;
986 while (!terminate) {
987 if (NULL != tls) {
988 if ((error = gnutls_init(&tls_session, GNUTLS_SERVER))) {
989 set_server_error("Could not initialize TLS session: %s",
990 gnutls_strerror(error));
991 terminate = -1;
992 continue;
994 if ((error = gnutls_priority_set(tls_session,
995 priority_cache))) {
996 set_server_error(
997 "Could not set priorities to TLS session: %s",
998 gnutls_strerror(error));
999 terminate = -1;
1000 gnutls_deinit(tls_session);
1001 continue;
1003 if ((error = gnutls_credentials_set(tls_session,
1004 GNUTLS_CRD_CERTIFICATE, x509_credentials))) {
1005 set_server_error("Could not set X509 credentials to TLS "
1006 "session: %s", gnutls_strerror(error));
1007 terminate = -1;
1008 gnutls_deinit(tls_session);
1009 continue;
1011 if (NULL != ticket_key.data) {
1012 if ((error = gnutls_session_ticket_enable_server(
1013 tls_session, &ticket_key))) {
1014 fprintf(stderr, "Could not register ticket key to "
1015 "TLS session: %s\n", gnutls_strerror(error));
1018 /* XXX: Credentials are linked from session now.
1019 * Deinitializition must free session before x509_credentials.
1021 if (NULL != tls->client_name) {
1022 client_required_dn = tls->client_name;
1023 /* Require client certificate */
1024 gnutls_certificate_server_set_request(tls_session,
1025 GNUTLS_CERT_REQUIRE);
1026 /* And verify it in TLS handshake */
1027 gnutls_certificate_set_verify_function(x509_credentials,
1028 tls_verify_client);
1032 if (0 > (client_socket = accept(server_socket, NULL, NULL))) {
1033 terminate = -1;
1034 if (NULL != tls)
1035 gnutls_deinit(tls_session);
1036 continue;
1038 fprintf(stderr, "Connection accepted\n");
1039 connection.socket = client_socket;
1041 if (NULL == tls) {
1042 connection.recv_callback = recv_plain;
1043 connection.send_callback = send_plain;
1044 connection.callback_data = NULL;
1045 } else {
1046 connection.recv_callback = recv_tls;
1047 connection.send_callback = send_tls;
1048 connection.callback_data = tls_session;
1049 gnutls_transport_set_pull_function(tls_session, tls_pull);
1050 gnutls_transport_set_push_function(tls_session, tls_push);
1051 gnutls_transport_set_ptr2(tls_session,
1052 &connection, &connection);
1053 do {
1054 error = gnutls_handshake(tls_session);
1055 } while (error < 0 && !gnutls_error_is_fatal(error));
1056 if (error < 0) {
1057 fprintf(stderr, "TLS handshake failed: %s\n",
1058 gnutls_strerror(error));
1059 close(client_socket);
1060 gnutls_deinit(tls_session);
1061 continue;
1065 error = http_read_request(&connection, &request);
1066 if (error) {
1067 fprintf(stderr, "Error while reading request\n");
1068 if (error == HTTP_ERROR_CLIENT)
1069 http_send_response_400(&connection, "Error in request");
1070 else
1071 http_send_response_500(&connection,
1072 "Could not read request");
1073 close(client_socket);
1074 if (NULL != tls)
1075 gnutls_deinit(tls_session);
1076 continue;
1079 terminate = server_implementation(&connection, server_arguments,
1080 request);
1082 http_request_free(&request);
1083 close(client_socket);
1084 if (NULL != tls) {
1085 gnutls_deinit(tls_session);
1089 if (NULL != tls) {
1090 if (NULL != ticket_key.data) {
1091 gnutls_free(ticket_key.data);
1094 close(server_socket);
1095 free(authorization_cookie_value);
1096 exit(EXIT_SUCCESS);
1097 /* Does not return */
1100 return 0;
1104 /* Kill the server process.
1105 * Return 0. Return -1 if server could not been stopped. Return 1 if server
1106 * crashed. */
1107 int stop_server(pid_t server_process) {
1108 int status;
1109 if (server_process <= 0) {
1110 set_server_error("Invalid server PID to kill");
1111 return -1;
1113 if (-1 == kill(server_process, SIGTERM)) {
1114 set_server_error("Could not terminate server");
1115 return -1;
1117 if (-1 == waitpid(server_process, &status, 0)) {
1118 set_server_error("Could not wait for server termination");
1119 return -1;
1121 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
1122 set_server_error("Server terminated by signal %d violently",
1123 WTERMSIG(status));
1124 return 1;
1126 return 0;