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 2869 : TES_transmit_raw (int sock,
80 : size_t end,
81 : const void *pos)
82 : {
83 2869 : size_t off = 0;
84 :
85 2869 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
86 : "Sending message of length %u\n",
87 : (unsigned int) end);
88 5737 : while (off < end)
89 : {
90 2869 : ssize_t ret = send (sock,
91 : pos,
92 : end - off,
93 : 0 /* no flags => blocking! */);
94 :
95 2868 : 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 2868 : if (-1 == ret)
104 : {
105 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
106 : "send");
107 0 : return GNUNET_SYSERR;
108 : }
109 2868 : if (0 == ret)
110 : {
111 0 : GNUNET_break (0);
112 0 : return GNUNET_SYSERR;
113 : }
114 2868 : off += ret;
115 2868 : pos += ret;
116 : }
117 2868 : return GNUNET_OK;
118 : }
119 :
120 :
121 : enum GNUNET_GenericReturnValue
122 2845 : TES_transmit (int sock,
123 : const struct GNUNET_MessageHeader *hdr)
124 : {
125 2845 : 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 5689 : return TES_transmit_raw (sock,
130 2845 : ntohs (hdr->size),
131 : hdr);
132 : }
133 :
134 :
135 : struct GNUNET_NETWORK_Handle *
136 3 : TES_open_socket (const char *unixpath)
137 : {
138 : int sock;
139 : mode_t old_umask;
140 3 : 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 3 : old_umask = umask (S_IROTH | S_IWOTH | S_IXOTH);
148 :
149 3 : sock = socket (PF_UNIX,
150 : SOCK_STREAM,
151 : 0);
152 3 : 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 3 : if (GNUNET_OK !=
162 3 : GNUNET_DISK_directory_create_for_file (unixpath))
163 : {
164 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
165 : "mkdir(dirname)",
166 : unixpath);
167 : }
168 3 : if (0 != unlink (unixpath))
169 : {
170 3 : if (ENOENT != errno)
171 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
172 : "unlink",
173 : unixpath);
174 : }
175 3 : memset (&un,
176 : 0,
177 : sizeof (un));
178 3 : un.sun_family = AF_UNIX;
179 3 : strncpy (un.sun_path,
180 : unixpath,
181 : sizeof (un.sun_path) - 1);
182 3 : 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 : unixpath);
189 0 : GNUNET_break (0 == close (sock));
190 0 : goto cleanup;
191 : }
192 3 : ret = GNUNET_NETWORK_socket_box_native (sock);
193 3 : if (GNUNET_OK !=
194 3 : GNUNET_NETWORK_socket_listen (ret,
195 : 512))
196 : {
197 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
198 : "listen",
199 : unixpath);
200 0 : GNUNET_break (GNUNET_OK ==
201 : GNUNET_NETWORK_socket_close (ret));
202 0 : ret = NULL;
203 : }
204 : }
205 3 : cleanup:
206 3 : (void) umask (old_umask);
207 3 : return ret;
208 : }
209 :
210 :
211 : void
212 13 : TES_wake_clients (void)
213 : {
214 13 : uint64_t num = 1;
215 :
216 13 : GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
217 22 : for (struct TES_Client *client = TES_clients_head;
218 : NULL != client;
219 9 : client = client->next)
220 : {
221 9 : GNUNET_assert (sizeof (num) ==
222 : #ifdef __linux__
223 : write (client->esock,
224 : #else
225 : write (client->esock_in,
226 : #endif
227 : &num,
228 : sizeof (num)));
229 : }
230 13 : GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
231 13 : }
232 :
233 :
234 : enum GNUNET_GenericReturnValue
235 2770 : TES_read_work (void *cls,
236 : TES_MessageDispatch dispatch)
237 : {
238 2770 : struct TES_Client *client = cls;
239 2770 : char *buf = client->iobuf;
240 2770 : size_t off = 0;
241 2770 : uint16_t msize = 0;
242 2770 : const struct GNUNET_MessageHeader *hdr = NULL;
243 : enum GNUNET_GenericReturnValue ret;
244 :
245 : do
246 : {
247 : ssize_t recv_size;
248 :
249 2771 : recv_size = recv (client->csock,
250 2771 : &buf[off],
251 : sizeof (client->iobuf) - off,
252 : 0);
253 2769 : if (-1 == recv_size)
254 : {
255 3 : if ( (0 == off) &&
256 0 : (EAGAIN == errno) )
257 0 : return GNUNET_NO;
258 3 : if ( (EINTR == errno) ||
259 0 : (EAGAIN == errno) )
260 : {
261 3 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
262 : "recv");
263 0 : continue;
264 : }
265 0 : if (ECONNRESET != errno)
266 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
267 : "recv");
268 0 : return GNUNET_SYSERR;
269 : }
270 2766 : if (0 == recv_size)
271 : {
272 : /* regular disconnect? */
273 27 : GNUNET_break_op (0 == off);
274 27 : return GNUNET_SYSERR;
275 : }
276 2739 : off += recv_size;
277 2739 : more:
278 2739 : if (off < sizeof (struct GNUNET_MessageHeader))
279 0 : continue;
280 2739 : hdr = (const struct GNUNET_MessageHeader *) buf;
281 2739 : msize = ntohs (hdr->size);
282 : #if 0
283 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
284 : "Received message of type %u with %u bytes\n",
285 : (unsigned int) ntohs (hdr->type),
286 : (unsigned int) msize);
287 : #endif
288 2739 : if (msize < sizeof (struct GNUNET_MessageHeader))
289 : {
290 0 : GNUNET_break_op (0);
291 0 : return GNUNET_SYSERR;
292 : }
293 2739 : } while (off < msize);
294 :
295 2738 : ret = dispatch (client,
296 : hdr);
297 2742 : if ( (GNUNET_OK != ret) ||
298 2742 : (off == msize) )
299 2742 : return ret;
300 0 : memmove (buf,
301 0 : &buf[msize],
302 : off - msize);
303 0 : off -= msize;
304 0 : goto more;
305 : }
306 :
307 :
308 : bool
309 2769 : TES_await_ready (struct TES_Client *client)
310 : {
311 : /* wait for reply with 1s timeout */
312 2769 : struct pollfd pfds[] = {
313 : {
314 2769 : .fd = client->csock,
315 : .events = POLLIN
316 : },
317 : {
318 : #ifdef __linux__
319 2769 : .fd = client->esock,
320 : #else
321 : .fd = client->esock_out,
322 : #endif
323 : .events = POLLIN
324 : },
325 : };
326 : int ret;
327 :
328 2769 : ret = poll (pfds,
329 : 2,
330 : -1);
331 2770 : if ( (-1 == ret) &&
332 0 : (EINTR != errno) )
333 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
334 : "poll");
335 8300 : for (int i = 0; i<2; i++)
336 : {
337 : #ifdef __linux__
338 5539 : if ( (pfds[i].fd == client->esock) &&
339 : #else
340 : if ( (pfds[i].fd == client->esock_out) &&
341 : #endif
342 2770 : (POLLIN == pfds[i].revents) )
343 : {
344 : uint64_t num;
345 :
346 9 : GNUNET_assert (sizeof (num) ==
347 : #ifdef __linux__
348 : read (client->esock,
349 : #else
350 : read (client->esock_out,
351 : #endif
352 : &num,
353 : sizeof (num)));
354 9 : return true;
355 : }
356 : }
357 2761 : return false;
358 : }
359 :
360 :
361 : void
362 27 : TES_free_client (struct TES_Client *client)
363 : {
364 27 : GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
365 27 : GNUNET_CONTAINER_DLL_remove (TES_clients_head,
366 : TES_clients_tail,
367 : client);
368 27 : GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
369 27 : GNUNET_break (0 == close (client->csock));
370 : #ifdef __linux__
371 27 : GNUNET_break (0 == close (client->esock));
372 : #else
373 : GNUNET_break (0 == close (client->esock_in));
374 : GNUNET_break (0 == close (client->esock_out));
375 : #endif
376 27 : pthread_detach (client->worker);
377 27 : GNUNET_free (client);
378 27 : }
379 :
380 :
381 : /**
382 : * Main function of a worker thread that signs.
383 : *
384 : * @param cls the client we are working on
385 : * @return NULL
386 : */
387 : static void *
388 27 : sign_worker (void *cls)
389 : {
390 27 : struct TES_Client *client = cls;
391 :
392 27 : if (GNUNET_OK !=
393 27 : client->cb.init (client))
394 : {
395 0 : GNUNET_break (0);
396 0 : TES_free_client (client);
397 0 : return NULL;
398 : }
399 2769 : while (! in_shutdown)
400 : {
401 2770 : if (TES_await_ready (client))
402 : {
403 9 : if (GNUNET_OK !=
404 9 : client->cb.updater (client))
405 0 : break;
406 : }
407 2769 : if (GNUNET_SYSERR ==
408 2770 : TES_read_work (client,
409 : client->cb.dispatch))
410 27 : break;
411 : }
412 26 : TES_free_client (client);
413 27 : return NULL;
414 : }
415 :
416 :
417 : /**
418 : * Task that listens for incoming clients.
419 : *
420 : * @param cls a `struct TES_Callbacks`
421 : */
422 : static void
423 27 : listen_job (void *cls)
424 : {
425 27 : const struct TES_Callbacks *cb = cls;
426 : int s;
427 : #ifdef __linux__
428 : int e;
429 : #else
430 : int e[2];
431 : #endif
432 : struct sockaddr_storage sa;
433 27 : socklen_t sa_len = sizeof (sa);
434 :
435 27 : listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
436 : unix_sock,
437 : &listen_job,
438 : cls);
439 27 : s = accept (GNUNET_NETWORK_get_fd (unix_sock),
440 : (struct sockaddr *) &sa,
441 : &sa_len);
442 27 : if (-1 == s)
443 : {
444 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
445 : "accept");
446 0 : return;
447 : }
448 : #ifdef __linux__
449 27 : e = eventfd (0,
450 : EFD_CLOEXEC);
451 27 : if (-1 == e)
452 : {
453 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
454 : "eventfd");
455 0 : GNUNET_break (0 == close (s));
456 0 : return;
457 : }
458 : #else
459 : if (0 != pipe (e))
460 : {
461 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
462 : "pipe");
463 : GNUNET_break (0 == close (s));
464 : return;
465 : }
466 : #endif
467 : {
468 : struct TES_Client *client;
469 :
470 27 : client = GNUNET_new (struct TES_Client);
471 27 : client->cb = *cb;
472 27 : client->csock = s;
473 : #ifdef __linux__
474 27 : client->esock = e;
475 : #else
476 : client->esock_in = e[1];
477 : client->esock_out = e[0];
478 : #endif
479 27 : GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
480 27 : GNUNET_CONTAINER_DLL_insert (TES_clients_head,
481 : TES_clients_tail,
482 : client);
483 27 : GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
484 27 : if (0 !=
485 27 : pthread_create (&client->worker,
486 : NULL,
487 : &sign_worker,
488 : client))
489 : {
490 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
491 : "pthread_create");
492 0 : TES_free_client (client);
493 : }
494 : }
495 : }
496 :
497 :
498 : int
499 3 : 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 3 : if (GNUNET_OK !=
507 3 : 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 3 : if (GNUNET_SYSERR ==
518 3 : 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 3 : GNUNET_free (pfn);
530 3 : GNUNET_CRYPTO_eddsa_key_get_public (&TES_smpriv.eddsa_priv,
531 : &TES_smpub.eddsa_pub);
532 : }
533 :
534 :
535 3 : if (GNUNET_OK !=
536 3 : 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 3 : GNUNET_assert (NULL != unixpath);
547 3 : unix_sock = TES_open_socket (unixpath);
548 3 : 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 3 : listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
556 : unix_sock,
557 : &listen_job,
558 : (void *) cb);
559 3 : return 0;
560 : }
561 :
562 :
563 : void
564 3 : TES_listen_stop (void)
565 : {
566 3 : if (NULL != listen_task)
567 : {
568 3 : GNUNET_SCHEDULER_cancel (listen_task);
569 3 : listen_task = NULL;
570 : }
571 3 : if (NULL != unix_sock)
572 : {
573 3 : GNUNET_break (GNUNET_OK ==
574 : GNUNET_NETWORK_socket_close (unix_sock));
575 3 : unix_sock = NULL;
576 : }
577 3 : if (0 != unlink (unixpath))
578 : {
579 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
580 : "unlink",
581 : unixpath);
582 : }
583 3 : GNUNET_free (unixpath);
584 3 : in_shutdown = true;
585 3 : TES_wake_clients ();
586 3 : }
|