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 (16270B)


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