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