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