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


      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 char *
     31 TES_normalize_section (const char *section)
     32 {
     33   char *result = GNUNET_strdup (section);
     34 
     35   for (char *p = result; '\0' != *p; p++)
     36     *p = (char) tolower ((unsigned char) *p);
     37   return result;
     38 }
     39 
     40 
     41 /**
     42  * A denomination directory to rename after checking for conflicts.
     43  */
     44 struct KeyDirectory
     45 {
     46   char *old_name;
     47   char *new_name;
     48 };
     49 
     50 
     51 /**
     52  * Closure for #check_section_name and #collect_key_directory.
     53  */
     54 struct KeyDirectoryContext
     55 {
     56   const char *cprefix;
     57   struct KeyDirectory *dirs;
     58   unsigned int num_dirs;
     59   bool invalid_section_name;
     60 };
     61 
     62 
     63 /**
     64  * Reject section names that would use nested key directories.
     65  *
     66  * @param cls a `struct KeyDirectoryContext`
     67  * @param section configuration section name to check
     68  */
     69 static void
     70 check_section_name (void *cls,
     71                     const char *section)
     72 {
     73   struct KeyDirectoryContext *ctx = cls;
     74   size_t prefix_len = strlen (ctx->cprefix);
     75 
     76   if ( (0 != strncasecmp (section,
     77                           ctx->cprefix,
     78                           prefix_len)) ||
     79        (NULL == strchr (section + prefix_len, '/')) )
     80     return;
     81   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     82               "Invalid denomination section `%s': suffix must not contain '/'\n",
     83               section);
     84   ctx->invalid_section_name = true;
     85 }
     86 
     87 
     88 /**
     89  * Collect directories requiring migration without modifying the directory
     90  * being scanned. Reject symlinks and check all destinations before starting
     91  * the migration.
     92  *
     93  * @param cls a `struct KeyDirectoryContext`
     94  * @param filename directory entry to inspect
     95  * @return #GNUNET_OK to continue, #GNUNET_SYSERR on a symlink, conflict or I/O error
     96  */
     97 static enum GNUNET_GenericReturnValue
     98 collect_key_directory (void *cls,
     99                        const char *filename)
    100 {
    101   struct KeyDirectoryContext *ctx = cls;
    102   const char *base = strrchr (filename, '/') + 1;
    103   struct KeyDirectory dir;
    104   struct stat sb;
    105   struct stat target;
    106   char *normalized;
    107 
    108   if (0 != lstat (filename, &sb))
    109   {
    110     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    111                               "lstat",
    112                               filename);
    113     return GNUNET_SYSERR;
    114   }
    115   if (S_ISLNK (sb.st_mode))
    116   {
    117     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    118                 "Invalid key directory entry `%s': symbolic links are not allowed\n",
    119                 filename);
    120     return GNUNET_SYSERR;
    121   }
    122   if (0 != strncasecmp (base,
    123                         ctx->cprefix,
    124                         strlen (ctx->cprefix)))
    125     return GNUNET_OK;
    126   if (! S_ISDIR (sb.st_mode))
    127     return GNUNET_OK;
    128   normalized = TES_normalize_section (base);
    129   if (0 == strcmp (base, normalized))
    130   {
    131     GNUNET_free (normalized);
    132     return GNUNET_OK;
    133   }
    134   GNUNET_asprintf (&dir.new_name,
    135                    "%.*s%s",
    136                    (int) (base - filename),
    137                    filename,
    138                    normalized);
    139   GNUNET_free (normalized);
    140   for (unsigned int i = 0; i < ctx->num_dirs; i++)
    141   {
    142     if (0 != strcmp (dir.new_name, ctx->dirs[i].new_name))
    143       continue;
    144     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    145                 "Conflicting denomination key directories `%s' and `%s'; resolve their capitalization before restarting\n",
    146                 filename,
    147                 ctx->dirs[i].old_name);
    148     GNUNET_free (dir.new_name);
    149     return GNUNET_SYSERR;
    150   }
    151   if (0 == lstat (dir.new_name, &target))
    152   {
    153     /* On a case-insensitive filesystem both spellings may already refer to
    154        the same directory. Still rename it to update the stored spelling. */
    155     if ( (sb.st_dev != target.st_dev) ||
    156          (sb.st_ino != target.st_ino) )
    157     {
    158       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    159                   "Cannot rename denomination key directory `%s' to `%s': destination exists; resolve the conflict before restarting\n",
    160                   filename,
    161                   dir.new_name);
    162       GNUNET_free (dir.new_name);
    163       return GNUNET_SYSERR;
    164     }
    165   }
    166   else if (ENOENT != errno)
    167   {
    168     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    169                               "lstat",
    170                               dir.new_name);
    171     GNUNET_free (dir.new_name);
    172     return GNUNET_SYSERR;
    173   }
    174   dir.old_name = GNUNET_strdup (filename);
    175   GNUNET_array_append (ctx->dirs, ctx->num_dirs, dir);
    176   return GNUNET_OK;
    177 }
    178 
    179 
    180 enum GNUNET_GenericReturnValue
    181 TES_normalize_key_directory (const struct GNUNET_CONFIGURATION_Handle *cfg,
    182                              const char *keydir,
    183                              const char *cprefix)
    184 {
    185   struct KeyDirectoryContext ctx = {
    186     .cprefix = cprefix
    187   };
    188   enum GNUNET_GenericReturnValue ret = GNUNET_OK;
    189 
    190   GNUNET_CONFIGURATION_iterate_sections (cfg,
    191                                          &check_section_name,
    192                                          &ctx);
    193   if (ctx.invalid_section_name)
    194     return GNUNET_SYSERR;
    195   if (GNUNET_OK != GNUNET_DISK_directory_create (keydir))
    196     return GNUNET_SYSERR;
    197   if (0 > GNUNET_DISK_directory_scan (keydir,
    198                                      &collect_key_directory,
    199                                      &ctx))
    200     ret = GNUNET_SYSERR;
    201   for (unsigned int i = 0; i < ctx.num_dirs; i++)
    202   {
    203     struct KeyDirectory *dir = &ctx.dirs[i];
    204 
    205     if (GNUNET_OK == ret)
    206     {
    207       if (0 != rename (dir->old_name, dir->new_name))
    208       {
    209         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    210                                   "rename",
    211                                   dir->old_name);
    212         ret = GNUNET_SYSERR;
    213       }
    214       else
    215       {
    216         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    217                     "Renamed denomination key directory `%s' to `%s'\n",
    218                     dir->old_name,
    219                     dir->new_name);
    220       }
    221     }
    222     GNUNET_free (dir->old_name);
    223     GNUNET_free (dir->new_name);
    224   }
    225   GNUNET_free (ctx.dirs);
    226   return ret;
    227 }
    228 
    229 
    230 /**
    231  * Head of DLL of clients connected to us.
    232  */
    233 struct TES_Client *TES_clients_head;
    234 
    235 /**
    236  * Tail of DLL of clients connected to us.
    237  */
    238 struct TES_Client *TES_clients_tail;
    239 
    240 /**
    241  * Lock for the client queue.
    242  */
    243 pthread_mutex_t TES_clients_lock = PTHREAD_MUTEX_INITIALIZER;
    244 
    245 /**
    246  * Private key of this security module. Used to sign denomination key
    247  * announcements.
    248  */
    249 struct TALER_SecurityModulePrivateKeyP TES_smpriv;
    250 
    251 /**
    252  * Public key of this security module.
    253  */
    254 struct TALER_SecurityModulePublicKeyP TES_smpub;
    255 
    256 /**
    257  * Our listen socket.
    258  */
    259 static struct GNUNET_NETWORK_Handle *unix_sock;
    260 
    261 /**
    262  * Path where we are listening.
    263  */
    264 static char *unixpath;
    265 
    266 /**
    267  * Task run to accept new inbound connections.
    268  */
    269 static struct GNUNET_SCHEDULER_Task *listen_task;
    270 
    271 /**
    272  * Set once we are in shutdown and workers should terminate.
    273  */
    274 static volatile bool in_shutdown;
    275 
    276 
    277 enum GNUNET_GenericReturnValue
    278 TES_transmit_raw (int sock,
    279                   size_t end,
    280                   const void *pos)
    281 {
    282   size_t off = 0;
    283 
    284   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    285               "Sending message of length %u\n",
    286               (unsigned int) end);
    287   while (off < end)
    288   {
    289     ssize_t ret = send (sock,
    290                         pos,
    291                         end - off,
    292                         0 /* no flags => blocking! */);
    293 
    294     if ( (-1 == ret) &&
    295          ( (EAGAIN == errno) ||
    296            (EINTR == errno) ) )
    297     {
    298       GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
    299                            "send");
    300       continue;
    301     }
    302     if (-1 == ret)
    303     {
    304       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    305                            "send");
    306       return GNUNET_SYSERR;
    307     }
    308     if (0 == ret)
    309     {
    310       GNUNET_break (0);
    311       return GNUNET_SYSERR;
    312     }
    313     off += ret;
    314     pos += ret;
    315   }
    316   return GNUNET_OK;
    317 }
    318 
    319 
    320 enum GNUNET_GenericReturnValue
    321 TES_transmit (int sock,
    322               const struct GNUNET_MessageHeader *hdr)
    323 {
    324   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    325               "Sending message of type %u and length %u\n",
    326               (unsigned int) ntohs (hdr->type),
    327               (unsigned int) ntohs (hdr->size));
    328   return TES_transmit_raw (sock,
    329                            ntohs (hdr->size),
    330                            hdr);
    331 }
    332 
    333 
    334 struct GNUNET_NETWORK_Handle *
    335 TES_open_socket (const char *my_unixpath)
    336 {
    337   int sock;
    338   mode_t old_umask;
    339   struct GNUNET_NETWORK_Handle *ret = NULL;
    340 
    341   /* Change permissions so that group read/writes are allowed.
    342    * We need this for multi-user exchange deployment with privilege
    343    * separation, where taler-exchange-httpd is part of a group
    344    * that allows it to talk to secmod.
    345    */
    346   old_umask = umask (S_IROTH | S_IWOTH | S_IXOTH);
    347 
    348   sock = socket (PF_UNIX,
    349                  SOCK_STREAM,
    350                  0);
    351   if (-1 == sock)
    352   {
    353     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    354                          "socket");
    355     goto cleanup;
    356   }
    357   {
    358     struct sockaddr_un un;
    359 
    360     if (GNUNET_OK !=
    361         GNUNET_DISK_directory_create_for_file (my_unixpath))
    362     {
    363       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    364                                 "mkdir(dirname)",
    365                                 my_unixpath);
    366     }
    367     if (0 != unlink (my_unixpath))
    368     {
    369       if (ENOENT != errno)
    370         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    371                                   "unlink",
    372                                   my_unixpath);
    373     }
    374     memset (&un,
    375             0,
    376             sizeof (un));
    377     un.sun_family = AF_UNIX;
    378     strncpy (un.sun_path,
    379              my_unixpath,
    380              sizeof (un.sun_path) - 1);
    381     if (0 != bind (sock,
    382                    (const struct sockaddr *) &un,
    383                    sizeof (un)))
    384     {
    385       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    386                                 "bind",
    387                                 my_unixpath);
    388       GNUNET_break (0 == close (sock));
    389       goto cleanup;
    390     }
    391     ret = GNUNET_NETWORK_socket_box_native (sock);
    392     if (GNUNET_OK !=
    393         GNUNET_NETWORK_socket_listen (ret,
    394                                       512))
    395     {
    396       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    397                                 "listen",
    398                                 my_unixpath);
    399       GNUNET_break (GNUNET_OK ==
    400                     GNUNET_NETWORK_socket_close (ret));
    401       ret = NULL;
    402     }
    403   }
    404 cleanup:
    405   (void) umask (old_umask);
    406   return ret;
    407 }
    408 
    409 
    410 void
    411 TES_wake_clients (void)
    412 {
    413   uint64_t num = 1;
    414 
    415   GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
    416   for (struct TES_Client *client = TES_clients_head;
    417        NULL != client;
    418        client = client->next)
    419   {
    420 #ifdef __linux__
    421     if (-1 == client->esock)
    422       continue;
    423     GNUNET_assert (sizeof (num) ==
    424                    write (client->esock,
    425                           &num,
    426                           sizeof (num)));
    427 #else
    428     if (-1 == client->esock_in)
    429       continue;
    430     GNUNET_assert (sizeof (num) ==
    431                    write (client->esock_in,
    432                           &num,
    433                           sizeof (num)));
    434 #endif
    435   }
    436   GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
    437 }
    438 
    439 
    440 enum GNUNET_GenericReturnValue
    441 TES_read_work (void *cls,
    442                TES_MessageDispatch dispatch)
    443 {
    444   struct TES_Client *client = cls;
    445   char *buf = client->iobuf;
    446   size_t off = 0;
    447   uint16_t msize = 0;
    448   const struct GNUNET_MessageHeader *hdr = NULL;
    449   enum GNUNET_GenericReturnValue ret;
    450 
    451   do
    452   {
    453     ssize_t recv_size;
    454 
    455     recv_size = recv (client->csock,
    456                       &buf[off],
    457                       sizeof (client->iobuf) - off,
    458                       0);
    459     if (-1 == recv_size)
    460     {
    461       if ( (0 == off) &&
    462            (EAGAIN == errno) )
    463         return GNUNET_NO;
    464       if ( (EINTR == errno) ||
    465            (EAGAIN == errno) )
    466       {
    467         GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
    468                              "recv");
    469         continue;
    470       }
    471       if (ECONNRESET != errno)
    472         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    473                              "recv");
    474       return GNUNET_SYSERR;
    475     }
    476     if (0 == recv_size)
    477     {
    478       /* regular disconnect? */
    479       GNUNET_break_op (0 == off);
    480       return GNUNET_SYSERR;
    481     }
    482     off += recv_size;
    483 more:
    484     msize = sizeof (*hdr); /* at least */
    485     if (off < msize)
    486       continue;
    487     hdr = (const struct GNUNET_MessageHeader *) buf;
    488     msize = ntohs (hdr->size);
    489 #if 0
    490     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    491                 "Received message of type %u with %u bytes\n",
    492                 (unsigned int) ntohs (hdr->type),
    493                 (unsigned int) msize);
    494 #endif
    495     if (msize < sizeof (*hdr))
    496     {
    497       GNUNET_break_op (0);
    498       return GNUNET_SYSERR;
    499     }
    500   } while (off < msize);
    501 
    502   ret = dispatch (client,
    503                   hdr);
    504   if ( (GNUNET_OK != ret) ||
    505        (off == msize) )
    506     return ret;
    507   memmove (buf,
    508            &buf[msize],
    509            off - msize);
    510   off -= msize;
    511   goto more;
    512 }
    513 
    514 
    515 bool
    516 TES_await_ready (struct TES_Client *client)
    517 {
    518   /* wait for reply with 1s timeout */
    519   struct pollfd pfds[] = {
    520     {
    521       .fd = client->csock,
    522       .events = POLLIN
    523     },
    524     {
    525 #ifdef __linux__
    526       .fd = client->esock,
    527 #else
    528       .fd = client->esock_out,
    529 #endif
    530       .events = POLLIN
    531     },
    532   };
    533   int ret;
    534 
    535   ret = poll (pfds,
    536               2,
    537               -1);
    538   if ( (-1 == ret) &&
    539        (EINTR != errno) )
    540     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    541                          "poll");
    542   for (int i = 0; i<2; i++)
    543   {
    544     if (
    545 #ifdef __linux__
    546       (pfds[i].fd == client->esock) &&
    547 #else
    548       (pfds[i].fd == client->esock_out) &&
    549 #endif
    550       (POLLIN == pfds[i].revents) )
    551     {
    552       uint64_t num;
    553 
    554 #ifdef __linux__
    555       GNUNET_assert (sizeof (num) ==
    556                      read (client->esock,
    557                            &num,
    558                            sizeof (num)));
    559 #else
    560       GNUNET_assert (sizeof (num) ==
    561                      read (client->esock_out,
    562                            &num,
    563                            sizeof (num)));
    564 #endif
    565       return true;
    566     }
    567   }
    568   return false;
    569 }
    570 
    571 
    572 /**
    573  * Main function of a worker thread that signs.
    574  *
    575  * @param cls the client we are working on
    576  * @return NULL
    577  */
    578 static void *
    579 sign_worker (void *cls)
    580 {
    581   struct TES_Client *client = cls;
    582 
    583   if (GNUNET_OK !=
    584       client->cb.init (client))
    585   {
    586     GNUNET_break (0);
    587     return NULL;
    588   }
    589   while (! in_shutdown)
    590   {
    591     if (TES_await_ready (client))
    592     {
    593       if (GNUNET_OK !=
    594           client->cb.updater (client))
    595         break;
    596     }
    597     else
    598     {
    599       if (GNUNET_SYSERR ==
    600           TES_read_work (client,
    601                          client->cb.dispatch))
    602         break;
    603     }
    604   }
    605   GNUNET_break (0 == close (client->csock));
    606   client->csock = -1;
    607   return NULL;
    608 }
    609 
    610 
    611 /**
    612  * Clean up @a pos, joining the thread and closing the
    613  * file descriptors.
    614  *
    615  * @param[in] pos client to clean up
    616  */
    617 static void
    618 join_client (struct TES_Client *pos)
    619 {
    620   void *rval;
    621 
    622   GNUNET_CONTAINER_DLL_remove (TES_clients_head,
    623                                TES_clients_tail,
    624                                pos);
    625   GNUNET_break (0 ==
    626                 pthread_join (pos->worker,
    627                               &rval));
    628 #ifdef __linux__
    629   GNUNET_break (0 == close (pos->esock));
    630   pos->esock = -1;
    631 #else
    632   GNUNET_break (0 == close (pos->esock_in));
    633   pos->esock_in = -1;
    634   GNUNET_break (0 == close (pos->esock_out));
    635   pos->esock_out = -1;
    636 #endif
    637   GNUNET_free (pos);
    638 }
    639 
    640 
    641 /**
    642  * Task that listens for incoming clients.
    643  *
    644  * @param cls a `struct TES_Callbacks`
    645  */
    646 static void
    647 listen_job (void *cls)
    648 {
    649   const struct TES_Callbacks *cb = cls;
    650   int s;
    651 #ifdef __linux__
    652   int e;
    653 #else
    654   int e[2];
    655 #endif
    656   struct sockaddr_storage sa;
    657   socklen_t sa_len = sizeof (sa);
    658 
    659   listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
    660                                                unix_sock,
    661                                                &listen_job,
    662                                                cls);
    663   s = accept (GNUNET_NETWORK_get_fd (unix_sock),
    664               (struct sockaddr *) &sa,
    665               &sa_len);
    666   if (-1 == s)
    667   {
    668     bool st = ( (ENFILE == errno) ||
    669                 (EMFILE == errno) );
    670     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    671                          "accept");
    672     if (st)
    673     {
    674       GNUNET_SCHEDULER_cancel (listen_task);
    675       listen_task = NULL;
    676     }
    677     return;
    678   }
    679 #ifdef __linux__
    680   e = eventfd (0,
    681                EFD_CLOEXEC);
    682   if (-1 == e)
    683   {
    684     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    685                          "eventfd");
    686     GNUNET_break (0 == close (s));
    687     return;
    688   }
    689 #else
    690   if (0 != pipe (e))
    691   {
    692     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    693                          "pipe");
    694     GNUNET_break (0 == close (s));
    695     return;
    696   }
    697 #endif
    698   {
    699     struct TES_Client *client;
    700     struct TES_Client *nxt;
    701 
    702     client = GNUNET_new (struct TES_Client);
    703     client->cb = *cb;
    704     client->csock = s;
    705 #ifdef __linux__
    706     client->esock = e;
    707 #else
    708     client->esock_in = e[1];
    709     client->esock_out = e[0];
    710 #endif
    711     GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
    712     for (struct TES_Client *pos = TES_clients_head;
    713          NULL != pos;
    714          pos = nxt)
    715     {
    716       nxt = pos->next;
    717       if (-1 == pos->csock)
    718       {
    719         join_client (pos);
    720       }
    721     }
    722     GNUNET_CONTAINER_DLL_insert (TES_clients_head,
    723                                  TES_clients_tail,
    724                                  client);
    725     GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
    726     if (0 !=
    727         pthread_create (&client->worker,
    728                         NULL,
    729                         &sign_worker,
    730                         client))
    731     {
    732       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    733                            "pthread_create");
    734       GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
    735       GNUNET_CONTAINER_DLL_remove (TES_clients_head,
    736                                    TES_clients_tail,
    737                                    client);
    738       GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
    739       GNUNET_break (0 == close (client->csock));
    740 #ifdef __linux__
    741       GNUNET_break (0 == close (client->esock));
    742 #else
    743       GNUNET_break (0 == close (client->esock_in));
    744       GNUNET_break (0 == close (client->esock_out));
    745 #endif
    746       GNUNET_free (client);
    747     }
    748   }
    749 }
    750 
    751 
    752 int
    753 TES_listen_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
    754                   const char *section,
    755                   const struct TES_Callbacks *cb)
    756 {
    757   {
    758     char *pfn;
    759 
    760     if (GNUNET_OK !=
    761         GNUNET_CONFIGURATION_get_value_filename (cfg,
    762                                                  section,
    763                                                  "SM_PRIV_KEY",
    764                                                  &pfn))
    765     {
    766       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    767                                  section,
    768                                  "SM_PRIV_KEY");
    769       return EXIT_NOTCONFIGURED;
    770     }
    771     if (GNUNET_SYSERR ==
    772         GNUNET_CRYPTO_eddsa_key_from_file (pfn,
    773                                            GNUNET_YES,
    774                                            &TES_smpriv.eddsa_priv))
    775     {
    776       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    777                                  section,
    778                                  "SM_PRIV_KEY",
    779                                  "Could not use file to persist private key");
    780       GNUNET_free (pfn);
    781       return EXIT_NOPERMISSION;
    782     }
    783     GNUNET_free (pfn);
    784     GNUNET_CRYPTO_eddsa_key_get_public (&TES_smpriv.eddsa_priv,
    785                                         &TES_smpub.eddsa_pub);
    786   }
    787 
    788   if (GNUNET_OK !=
    789       GNUNET_CONFIGURATION_get_value_filename (cfg,
    790                                                section,
    791                                                "UNIXPATH",
    792                                                &unixpath))
    793   {
    794     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    795                                section,
    796                                "UNIXPATH");
    797     return EXIT_NOTCONFIGURED;
    798   }
    799   GNUNET_assert (NULL != unixpath);
    800   unix_sock = TES_open_socket (unixpath);
    801   if (NULL == unix_sock)
    802   {
    803     GNUNET_free (unixpath);
    804     GNUNET_break (0);
    805     return EXIT_NOPERMISSION;
    806   }
    807   /* start job to accept incoming requests on 'sock' */
    808   listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
    809                                                unix_sock,
    810                                                &listen_job,
    811                                                (void *) cb);
    812   return 0;
    813 }
    814 
    815 
    816 void
    817 TES_listen_stop (void)
    818 {
    819   struct TES_Client *client;
    820 
    821   if (NULL != listen_task)
    822   {
    823     GNUNET_SCHEDULER_cancel (listen_task);
    824     listen_task = NULL;
    825   }
    826   if (NULL != unix_sock)
    827   {
    828     GNUNET_break (GNUNET_OK ==
    829                   GNUNET_NETWORK_socket_close (unix_sock));
    830     unix_sock = NULL;
    831   }
    832   if (0 != unlink (unixpath))
    833   {
    834     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    835                               "unlink",
    836                               unixpath);
    837   }
    838   GNUNET_free (unixpath);
    839   in_shutdown = true;
    840   TES_wake_clients ();
    841   GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
    842   while (NULL != (client = TES_clients_head))
    843     join_client (client);
    844   GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
    845 }