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