LCOV - code coverage report
Current view: top level - util - secmod_common.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 145 201 72.1 %
Date: 2022-08-25 06:15:09 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2020 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file util/secmod_common.c
      18             :  * @brief Common functions for the exchange security modules
      19             :  * @author Florian Dold <dold@taler.net>
      20             :  */
      21             : #include "platform.h"
      22             : #include "taler_util.h"
      23             : #include "taler_signatures.h"
      24             : #include "secmod_common.h"
      25             : #include <poll.h>
      26             : #ifdef __linux__
      27             : #include <sys/eventfd.h>
      28             : #endif
      29             : 
      30             : 
      31             : /**
      32             :  * Head of DLL of clients connected to us.
      33             :  */
      34             : struct TES_Client *TES_clients_head;
      35             : 
      36             : /**
      37             :  * Tail of DLL of clients connected to us.
      38             :  */
      39             : struct TES_Client *TES_clients_tail;
      40             : 
      41             : /**
      42             :  * Lock for the client queue.
      43             :  */
      44             : pthread_mutex_t TES_clients_lock;
      45             : 
      46             : /**
      47             :  * Private key of this security module. Used to sign denomination key
      48             :  * announcements.
      49             :  */
      50             : struct TALER_SecurityModulePrivateKeyP TES_smpriv;
      51             : 
      52             : /**
      53             :  * Public key of this security module.
      54             :  */
      55             : struct TALER_SecurityModulePublicKeyP TES_smpub;
      56             : 
      57             : /**
      58             :  * Our listen socket.
      59             :  */
      60             : static struct GNUNET_NETWORK_Handle *unix_sock;
      61             : 
      62             : /**
      63             :  * Path where we are listening.
      64             :  */
      65             : static char *unixpath;
      66             : 
      67             : /**
      68             :  * Task run to accept new inbound connections.
      69             :  */
      70             : static struct GNUNET_SCHEDULER_Task *listen_task;
      71             : 
      72             : /**
      73             :  * Set once we are in shutdown and workers should terminate.
      74             :  */
      75             : static volatile bool in_shutdown;
      76             : 
      77             : 
      78             : enum GNUNET_GenericReturnValue
      79        2869 : TES_transmit_raw (int sock,
      80             :                   size_t end,
      81             :                   const void *pos)
      82             : {
      83        2869 :   size_t off = 0;
      84             : 
      85        2869 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
      86             :               "Sending message of length %u\n",
      87             :               (unsigned int) end);
      88        5737 :   while (off < end)
      89             :   {
      90        2869 :     ssize_t ret = send (sock,
      91             :                         pos,
      92             :                         end - off,
      93             :                         0 /* no flags => blocking! */);
      94             : 
      95        2868 :     if ( (-1 == ret) &&
      96           0 :          ( (EAGAIN == errno) ||
      97           0 :            (EINTR == errno) ) )
      98             :     {
      99           0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
     100             :                            "send");
     101           0 :       continue;
     102             :     }
     103        2868 :     if (-1 == ret)
     104             :     {
     105           0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     106             :                            "send");
     107           0 :       return GNUNET_SYSERR;
     108             :     }
     109        2868 :     if (0 == ret)
     110             :     {
     111           0 :       GNUNET_break (0);
     112           0 :       return GNUNET_SYSERR;
     113             :     }
     114        2868 :     off += ret;
     115        2868 :     pos += ret;
     116             :   }
     117        2868 :   return GNUNET_OK;
     118             : }
     119             : 
     120             : 
     121             : enum GNUNET_GenericReturnValue
     122        2845 : TES_transmit (int sock,
     123             :               const struct GNUNET_MessageHeader *hdr)
     124             : {
     125        2845 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     126             :               "Sending message of type %u and length %u\n",
     127             :               (unsigned int) ntohs (hdr->type),
     128             :               (unsigned int) ntohs (hdr->size));
     129        5689 :   return TES_transmit_raw (sock,
     130        2845 :                            ntohs (hdr->size),
     131             :                            hdr);
     132             : }
     133             : 
     134             : 
     135             : struct GNUNET_NETWORK_Handle *
     136           3 : TES_open_socket (const char *unixpath)
     137             : {
     138             :   int sock;
     139             :   mode_t old_umask;
     140           3 :   struct GNUNET_NETWORK_Handle *ret = NULL;
     141             : 
     142             :   /* Change permissions so that group read/writes are allowed.
     143             :    * We need this for multi-user exchange deployment with privilege
     144             :    * separation, where taler-exchange-httpd is part of a group
     145             :    * that allows it to talk to secmod.
     146             :    */
     147           3 :   old_umask = umask (S_IROTH | S_IWOTH | S_IXOTH);
     148             : 
     149           3 :   sock = socket (PF_UNIX,
     150             :                  SOCK_STREAM,
     151             :                  0);
     152           3 :   if (-1 == sock)
     153             :   {
     154           0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     155             :                          "socket");
     156           0 :     goto cleanup;
     157             :   }
     158             :   {
     159             :     struct sockaddr_un un;
     160             : 
     161           3 :     if (GNUNET_OK !=
     162           3 :         GNUNET_DISK_directory_create_for_file (unixpath))
     163             :     {
     164           0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     165             :                                 "mkdir(dirname)",
     166             :                                 unixpath);
     167             :     }
     168           3 :     if (0 != unlink (unixpath))
     169             :     {
     170           3 :       if (ENOENT != errno)
     171           0 :         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     172             :                                   "unlink",
     173             :                                   unixpath);
     174             :     }
     175           3 :     memset (&un,
     176             :             0,
     177             :             sizeof (un));
     178           3 :     un.sun_family = AF_UNIX;
     179           3 :     strncpy (un.sun_path,
     180             :              unixpath,
     181             :              sizeof (un.sun_path) - 1);
     182           3 :     if (0 != bind (sock,
     183             :                    (const struct sockaddr *) &un,
     184             :                    sizeof (un)))
     185             :     {
     186           0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     187             :                                 "bind",
     188             :                                 unixpath);
     189           0 :       GNUNET_break (0 == close (sock));
     190           0 :       goto cleanup;
     191             :     }
     192           3 :     ret = GNUNET_NETWORK_socket_box_native (sock);
     193           3 :     if (GNUNET_OK !=
     194           3 :         GNUNET_NETWORK_socket_listen (ret,
     195             :                                       512))
     196             :     {
     197           0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     198             :                                 "listen",
     199             :                                 unixpath);
     200           0 :       GNUNET_break (GNUNET_OK ==
     201             :                     GNUNET_NETWORK_socket_close (ret));
     202           0 :       ret = NULL;
     203             :     }
     204             :   }
     205           3 : cleanup:
     206           3 :   (void) umask (old_umask);
     207           3 :   return ret;
     208             : }
     209             : 
     210             : 
     211             : void
     212          13 : TES_wake_clients (void)
     213             : {
     214          13 :   uint64_t num = 1;
     215             : 
     216          13 :   GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
     217          22 :   for (struct TES_Client *client = TES_clients_head;
     218             :        NULL != client;
     219           9 :        client = client->next)
     220             :   {
     221           9 :     GNUNET_assert (sizeof (num) ==
     222             : #ifdef __linux__
     223             :                    write (client->esock,
     224             : #else
     225             :                    write (client->esock_in,
     226             : #endif
     227             :                           &num,
     228             :                           sizeof (num)));
     229             :   }
     230          13 :   GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
     231          13 : }
     232             : 
     233             : 
     234             : enum GNUNET_GenericReturnValue
     235        2770 : TES_read_work (void *cls,
     236             :                TES_MessageDispatch dispatch)
     237             : {
     238        2770 :   struct TES_Client *client = cls;
     239        2770 :   char *buf = client->iobuf;
     240        2770 :   size_t off = 0;
     241        2770 :   uint16_t msize = 0;
     242        2770 :   const struct GNUNET_MessageHeader *hdr = NULL;
     243             :   enum GNUNET_GenericReturnValue ret;
     244             : 
     245             :   do
     246             :   {
     247             :     ssize_t recv_size;
     248             : 
     249        2771 :     recv_size = recv (client->csock,
     250        2771 :                       &buf[off],
     251             :                       sizeof (client->iobuf) - off,
     252             :                               0);
     253        2769 :     if (-1 == recv_size)
     254             :     {
     255           3 :       if ( (0 == off) &&
     256           0 :            (EAGAIN == errno) )
     257           0 :         return GNUNET_NO;
     258           3 :       if ( (EINTR == errno) ||
     259           0 :            (EAGAIN == errno) )
     260             :       {
     261           3 :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
     262             :                              "recv");
     263           0 :         continue;
     264             :       }
     265           0 :       if (ECONNRESET != errno)
     266           0 :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     267             :                              "recv");
     268           0 :       return GNUNET_SYSERR;
     269             :     }
     270        2766 :     if (0 == recv_size)
     271             :     {
     272             :       /* regular disconnect? */
     273          27 :       GNUNET_break_op (0 == off);
     274          27 :       return GNUNET_SYSERR;
     275             :     }
     276        2739 :     off += recv_size;
     277        2739 : more:
     278        2739 :     if (off < sizeof (struct GNUNET_MessageHeader))
     279           0 :       continue;
     280        2739 :     hdr = (const struct GNUNET_MessageHeader *) buf;
     281        2739 :     msize = ntohs (hdr->size);
     282             : #if 0
     283             :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     284             :                 "Received message of type %u with %u bytes\n",
     285             :                 (unsigned int) ntohs (hdr->type),
     286             :                 (unsigned int) msize);
     287             : #endif
     288        2739 :     if (msize < sizeof (struct GNUNET_MessageHeader))
     289             :     {
     290           0 :       GNUNET_break_op (0);
     291           0 :       return GNUNET_SYSERR;
     292             :     }
     293        2739 :   } while (off < msize);
     294             : 
     295        2738 :   ret = dispatch (client,
     296             :                   hdr);
     297        2742 :   if ( (GNUNET_OK != ret) ||
     298        2742 :        (off == msize) )
     299        2742 :     return ret;
     300           0 :   memmove (buf,
     301           0 :            &buf[msize],
     302             :            off - msize);
     303           0 :   off -= msize;
     304           0 :   goto more;
     305             : }
     306             : 
     307             : 
     308             : bool
     309        2769 : TES_await_ready (struct TES_Client *client)
     310             : {
     311             :   /* wait for reply with 1s timeout */
     312        2769 :   struct pollfd pfds[] = {
     313             :     {
     314        2769 :       .fd = client->csock,
     315             :       .events = POLLIN
     316             :     },
     317             :     {
     318             : #ifdef __linux__
     319        2769 :       .fd = client->esock,
     320             : #else
     321             :       .fd = client->esock_out,
     322             : #endif
     323             :       .events = POLLIN
     324             :     },
     325             :   };
     326             :   int ret;
     327             : 
     328        2769 :   ret = poll (pfds,
     329             :               2,
     330             :               -1);
     331        2770 :   if ( (-1 == ret) &&
     332           0 :        (EINTR != errno) )
     333           0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     334             :                          "poll");
     335        8300 :   for (int i = 0; i<2; i++)
     336             :   {
     337             : #ifdef __linux__
     338        5539 :     if ( (pfds[i].fd == client->esock) &&
     339             : #else
     340             :     if ( (pfds[i].fd == client->esock_out) &&
     341             : #endif
     342        2770 :          (POLLIN == pfds[i].revents) )
     343             :     {
     344             :       uint64_t num;
     345             : 
     346           9 :       GNUNET_assert (sizeof (num) ==
     347             : #ifdef __linux__
     348             :                      read (client->esock,
     349             : #else
     350             :                      read (client->esock_out,
     351             : #endif
     352             :                            &num,
     353             :                            sizeof (num)));
     354           9 :       return true;
     355             :     }
     356             :   }
     357        2761 :   return false;
     358             : }
     359             : 
     360             : 
     361             : void
     362          27 : TES_free_client (struct TES_Client *client)
     363             : {
     364          27 :   GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
     365          27 :   GNUNET_CONTAINER_DLL_remove (TES_clients_head,
     366             :                                TES_clients_tail,
     367             :                                client);
     368          27 :   GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
     369          27 :   GNUNET_break (0 == close (client->csock));
     370             : #ifdef __linux__
     371          27 :   GNUNET_break (0 == close (client->esock));
     372             : #else
     373             :   GNUNET_break (0 == close (client->esock_in));
     374             :   GNUNET_break (0 == close (client->esock_out));
     375             : #endif
     376          27 :   pthread_detach (client->worker);
     377          27 :   GNUNET_free (client);
     378          27 : }
     379             : 
     380             : 
     381             : /**
     382             :  * Main function of a worker thread that signs.
     383             :  *
     384             :  * @param cls the client we are working on
     385             :  * @return NULL
     386             :  */
     387             : static void *
     388          27 : sign_worker (void *cls)
     389             : {
     390          27 :   struct TES_Client *client = cls;
     391             : 
     392          27 :   if (GNUNET_OK !=
     393          27 :       client->cb.init (client))
     394             :   {
     395           0 :     GNUNET_break (0);
     396           0 :     TES_free_client (client);
     397           0 :     return NULL;
     398             :   }
     399        2769 :   while (! in_shutdown)
     400             :   {
     401        2770 :     if (TES_await_ready (client))
     402             :     {
     403           9 :       if (GNUNET_OK !=
     404           9 :           client->cb.updater (client))
     405           0 :         break;
     406             :     }
     407        2769 :     if (GNUNET_SYSERR ==
     408        2770 :         TES_read_work (client,
     409             :                        client->cb.dispatch))
     410          27 :       break;
     411             :   }
     412          26 :   TES_free_client (client);
     413          27 :   return NULL;
     414             : }
     415             : 
     416             : 
     417             : /**
     418             :  * Task that listens for incoming clients.
     419             :  *
     420             :  * @param cls a `struct TES_Callbacks`
     421             :  */
     422             : static void
     423          27 : listen_job (void *cls)
     424             : {
     425          27 :   const struct TES_Callbacks *cb = cls;
     426             :   int s;
     427             : #ifdef __linux__
     428             :   int e;
     429             : #else
     430             :   int e[2];
     431             : #endif
     432             :   struct sockaddr_storage sa;
     433          27 :   socklen_t sa_len = sizeof (sa);
     434             : 
     435          27 :   listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
     436             :                                                unix_sock,
     437             :                                                &listen_job,
     438             :                                                cls);
     439          27 :   s = accept (GNUNET_NETWORK_get_fd (unix_sock),
     440             :               (struct sockaddr *) &sa,
     441             :               &sa_len);
     442          27 :   if (-1 == s)
     443             :   {
     444           0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     445             :                          "accept");
     446           0 :     return;
     447             :   }
     448             : #ifdef __linux__
     449          27 :   e = eventfd (0,
     450             :                EFD_CLOEXEC);
     451          27 :   if (-1 == e)
     452             :   {
     453           0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     454             :                          "eventfd");
     455           0 :     GNUNET_break (0 == close (s));
     456           0 :     return;
     457             :   }
     458             : #else
     459             :   if (0 != pipe (e))
     460             :   {
     461             :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     462             :                          "pipe");
     463             :     GNUNET_break (0 == close (s));
     464             :     return;
     465             :   }
     466             : #endif
     467             :   {
     468             :     struct TES_Client *client;
     469             : 
     470          27 :     client = GNUNET_new (struct TES_Client);
     471          27 :     client->cb = *cb;
     472          27 :     client->csock = s;
     473             : #ifdef __linux__
     474          27 :     client->esock = e;
     475             : #else
     476             :     client->esock_in = e[1];
     477             :     client->esock_out = e[0];
     478             : #endif
     479          27 :     GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
     480          27 :     GNUNET_CONTAINER_DLL_insert (TES_clients_head,
     481             :                                  TES_clients_tail,
     482             :                                  client);
     483          27 :     GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
     484          27 :     if (0 !=
     485          27 :         pthread_create (&client->worker,
     486             :                         NULL,
     487             :                         &sign_worker,
     488             :                         client))
     489             :     {
     490           0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     491             :                            "pthread_create");
     492           0 :       TES_free_client (client);
     493             :     }
     494             :   }
     495             : }
     496             : 
     497             : 
     498             : int
     499           3 : TES_listen_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
     500             :                   const char *section,
     501             :                   const struct TES_Callbacks *cb)
     502             : {
     503             :   {
     504             :     char *pfn;
     505             : 
     506           3 :     if (GNUNET_OK !=
     507           3 :         GNUNET_CONFIGURATION_get_value_filename (cfg,
     508             :                                                  section,
     509             :                                                  "SM_PRIV_KEY",
     510             :                                                  &pfn))
     511             :     {
     512           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     513             :                                  section,
     514             :                                  "SM_PRIV_KEY");
     515           0 :       return EXIT_NOTCONFIGURED;
     516             :     }
     517           3 :     if (GNUNET_SYSERR ==
     518           3 :         GNUNET_CRYPTO_eddsa_key_from_file (pfn,
     519             :                                            GNUNET_YES,
     520             :                                            &TES_smpriv.eddsa_priv))
     521             :     {
     522           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     523             :                                  section,
     524             :                                  "SM_PRIV_KEY",
     525             :                                  "Could not use file to persist private key");
     526           0 :       GNUNET_free (pfn);
     527           0 :       return EXIT_NOPERMISSION;
     528             :     }
     529           3 :     GNUNET_free (pfn);
     530           3 :     GNUNET_CRYPTO_eddsa_key_get_public (&TES_smpriv.eddsa_priv,
     531             :                                         &TES_smpub.eddsa_pub);
     532             :   }
     533             : 
     534             : 
     535           3 :   if (GNUNET_OK !=
     536           3 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
     537             :                                                section,
     538             :                                                "UNIXPATH",
     539             :                                                &unixpath))
     540             :   {
     541           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     542             :                                section,
     543             :                                "UNIXPATH");
     544           0 :     return EXIT_NOTCONFIGURED;
     545             :   }
     546           3 :   GNUNET_assert (NULL != unixpath);
     547           3 :   unix_sock = TES_open_socket (unixpath);
     548           3 :   if (NULL == unix_sock)
     549             :   {
     550           0 :     GNUNET_free (unixpath);
     551           0 :     GNUNET_break (0);
     552           0 :     return EXIT_NOPERMISSION;
     553             :   }
     554             :   /* start job to accept incoming requests on 'sock' */
     555           3 :   listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
     556             :                                                unix_sock,
     557             :                                                &listen_job,
     558             :                                                (void *) cb);
     559           3 :   return 0;
     560             : }
     561             : 
     562             : 
     563             : void
     564           3 : TES_listen_stop (void)
     565             : {
     566           3 :   if (NULL != listen_task)
     567             :   {
     568           3 :     GNUNET_SCHEDULER_cancel (listen_task);
     569           3 :     listen_task = NULL;
     570             :   }
     571           3 :   if (NULL != unix_sock)
     572             :   {
     573           3 :     GNUNET_break (GNUNET_OK ==
     574             :                   GNUNET_NETWORK_socket_close (unix_sock));
     575           3 :     unix_sock = NULL;
     576             :   }
     577           3 :   if (0 != unlink (unixpath))
     578             :   {
     579           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     580             :                               "unlink",
     581             :                               unixpath);
     582             :   }
     583           3 :   GNUNET_free (unixpath);
     584           3 :   in_shutdown = true;
     585           3 :   TES_wake_clients ();
     586           3 : }

Generated by: LCOV version 1.14