exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

secmod_common.c (16228B)


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