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


      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 "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 #ifdef __linux__
    408   GNUNET_break (0 == close (client->esock));
    409   client->esock = -1;
    410 #else
    411   GNUNET_break (0 == close (client->esock_in));
    412   client->esock_in = -1;
    413   GNUNET_break (0 == close (client->esock_out));
    414   client->esock_out = -1;
    415 #endif
    416   return NULL;
    417 }
    418 
    419 
    420 /**
    421  * Task that listens for incoming clients.
    422  *
    423  * @param cls a `struct TES_Callbacks`
    424  */
    425 static void
    426 listen_job (void *cls)
    427 {
    428   const struct TES_Callbacks *cb = cls;
    429   int s;
    430 #ifdef __linux__
    431   int e;
    432 #else
    433   int e[2];
    434 #endif
    435   struct sockaddr_storage sa;
    436   socklen_t sa_len = sizeof (sa);
    437 
    438   listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
    439                                                unix_sock,
    440                                                &listen_job,
    441                                                cls);
    442   s = accept (GNUNET_NETWORK_get_fd (unix_sock),
    443               (struct sockaddr *) &sa,
    444               &sa_len);
    445   if (-1 == s)
    446   {
    447     bool st = ( (ENFILE == errno) ||
    448                 (EMFILE == errno) );
    449     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    450                          "accept");
    451     if (st)
    452     {
    453       GNUNET_SCHEDULER_cancel (listen_task);
    454       listen_task = NULL;
    455     }
    456     return;
    457   }
    458 #ifdef __linux__
    459   e = eventfd (0,
    460                EFD_CLOEXEC);
    461   if (-1 == e)
    462   {
    463     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    464                          "eventfd");
    465     GNUNET_break (0 == close (s));
    466     return;
    467   }
    468 #else
    469   if (0 != pipe (e))
    470   {
    471     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    472                          "pipe");
    473     GNUNET_break (0 == close (s));
    474     return;
    475   }
    476 #endif
    477   {
    478     struct TES_Client *client;
    479     struct TES_Client *nxt;
    480 
    481     client = GNUNET_new (struct TES_Client);
    482     client->cb = *cb;
    483     client->csock = s;
    484 #ifdef __linux__
    485     client->esock = e;
    486 #else
    487     client->esock_in = e[1];
    488     client->esock_out = e[0];
    489 #endif
    490     GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
    491     for (struct TES_Client *pos = TES_clients_head;
    492          NULL != pos;
    493          pos = nxt)
    494     {
    495       nxt = pos->next;
    496       if (-1 == pos->csock)
    497       {
    498         void *rval;
    499 
    500         GNUNET_CONTAINER_DLL_remove (TES_clients_head,
    501                                      TES_clients_tail,
    502                                      pos);
    503         GNUNET_break (0 ==
    504                       pthread_join (pos->worker,
    505                                     &rval));
    506         GNUNET_free (pos);
    507       }
    508     }
    509     GNUNET_CONTAINER_DLL_insert (TES_clients_head,
    510                                  TES_clients_tail,
    511                                  client);
    512     GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
    513     if (0 !=
    514         pthread_create (&client->worker,
    515                         NULL,
    516                         &sign_worker,
    517                         client))
    518     {
    519       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    520                            "pthread_create");
    521       GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
    522       GNUNET_CONTAINER_DLL_remove (TES_clients_head,
    523                                    TES_clients_tail,
    524                                    client);
    525       GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
    526       GNUNET_break (0 == close (client->csock));
    527 #ifdef __linux__
    528       GNUNET_break (0 == close (client->esock));
    529 #else
    530       GNUNET_break (0 == close (client->esock_in));
    531       GNUNET_break (0 == close (client->esock_out));
    532 #endif
    533       GNUNET_free (client);
    534     }
    535   }
    536 }
    537 
    538 
    539 int
    540 TES_listen_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
    541                   const char *section,
    542                   const struct TES_Callbacks *cb)
    543 {
    544   {
    545     char *pfn;
    546 
    547     if (GNUNET_OK !=
    548         GNUNET_CONFIGURATION_get_value_filename (cfg,
    549                                                  section,
    550                                                  "SM_PRIV_KEY",
    551                                                  &pfn))
    552     {
    553       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    554                                  section,
    555                                  "SM_PRIV_KEY");
    556       return EXIT_NOTCONFIGURED;
    557     }
    558     if (GNUNET_SYSERR ==
    559         GNUNET_CRYPTO_eddsa_key_from_file (pfn,
    560                                            GNUNET_YES,
    561                                            &TES_smpriv.eddsa_priv))
    562     {
    563       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    564                                  section,
    565                                  "SM_PRIV_KEY",
    566                                  "Could not use file to persist private key");
    567       GNUNET_free (pfn);
    568       return EXIT_NOPERMISSION;
    569     }
    570     GNUNET_free (pfn);
    571     GNUNET_CRYPTO_eddsa_key_get_public (&TES_smpriv.eddsa_priv,
    572                                         &TES_smpub.eddsa_pub);
    573   }
    574 
    575   if (GNUNET_OK !=
    576       GNUNET_CONFIGURATION_get_value_filename (cfg,
    577                                                section,
    578                                                "UNIXPATH",
    579                                                &unixpath))
    580   {
    581     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    582                                section,
    583                                "UNIXPATH");
    584     return EXIT_NOTCONFIGURED;
    585   }
    586   GNUNET_assert (NULL != unixpath);
    587   unix_sock = TES_open_socket (unixpath);
    588   if (NULL == unix_sock)
    589   {
    590     GNUNET_free (unixpath);
    591     GNUNET_break (0);
    592     return EXIT_NOPERMISSION;
    593   }
    594   /* start job to accept incoming requests on 'sock' */
    595   listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
    596                                                unix_sock,
    597                                                &listen_job,
    598                                                (void *) cb);
    599   return 0;
    600 }
    601 
    602 
    603 void
    604 TES_listen_stop (void)
    605 {
    606   struct TES_Client *client;
    607 
    608   if (NULL != listen_task)
    609   {
    610     GNUNET_SCHEDULER_cancel (listen_task);
    611     listen_task = NULL;
    612   }
    613   if (NULL != unix_sock)
    614   {
    615     GNUNET_break (GNUNET_OK ==
    616                   GNUNET_NETWORK_socket_close (unix_sock));
    617     unix_sock = NULL;
    618   }
    619   if (0 != unlink (unixpath))
    620   {
    621     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    622                               "unlink",
    623                               unixpath);
    624   }
    625   GNUNET_free (unixpath);
    626   in_shutdown = true;
    627   TES_wake_clients ();
    628   GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
    629   while (NULL != (client = TES_clients_head))
    630   {
    631     void *rval;
    632 
    633     GNUNET_CONTAINER_DLL_remove (TES_clients_head,
    634                                  TES_clients_tail,
    635                                  client);
    636     GNUNET_break (0 ==
    637                   pthread_join (client->worker,
    638                                 &rval));
    639     GNUNET_free (client);
    640   }
    641   GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
    642 }