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


      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 = PTHREAD_MUTEX_INITIALIZER;
     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     msize = sizeof (*hdr); /* at least */
    285     if (off < msize)
    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 (*hdr))
    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 }