secmod_common.c (16228B)
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 /** 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 TES_transmit_raw (int sock, 79 size_t end, 80 const void *pos) 81 { 82 size_t off = 0; 83 84 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 85 "Sending message of length %u\n", 86 (unsigned int) end); 87 while (off < end) 88 { 89 ssize_t ret = send (sock, 90 pos, 91 end - off, 92 0 /* no flags => blocking! */); 93 94 if ( (-1 == ret) && 95 ( (EAGAIN == errno) || 96 (EINTR == errno) ) ) 97 { 98 GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, 99 "send"); 100 continue; 101 } 102 if (-1 == ret) 103 { 104 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 105 "send"); 106 return GNUNET_SYSERR; 107 } 108 if (0 == ret) 109 { 110 GNUNET_break (0); 111 return GNUNET_SYSERR; 112 } 113 off += ret; 114 pos += ret; 115 } 116 return GNUNET_OK; 117 } 118 119 120 enum GNUNET_GenericReturnValue 121 TES_transmit (int sock, 122 const struct GNUNET_MessageHeader *hdr) 123 { 124 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 return TES_transmit_raw (sock, 129 ntohs (hdr->size), 130 hdr); 131 } 132 133 134 struct GNUNET_NETWORK_Handle * 135 TES_open_socket (const char *my_unixpath) 136 { 137 int sock; 138 mode_t old_umask; 139 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 old_umask = umask (S_IROTH | S_IWOTH | S_IXOTH); 147 148 sock = socket (PF_UNIX, 149 SOCK_STREAM, 150 0); 151 if (-1 == sock) 152 { 153 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 154 "socket"); 155 goto cleanup; 156 } 157 { 158 struct sockaddr_un un; 159 160 if (GNUNET_OK != 161 GNUNET_DISK_directory_create_for_file (my_unixpath)) 162 { 163 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 164 "mkdir(dirname)", 165 my_unixpath); 166 } 167 if (0 != unlink (my_unixpath)) 168 { 169 if (ENOENT != errno) 170 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 171 "unlink", 172 my_unixpath); 173 } 174 memset (&un, 175 0, 176 sizeof (un)); 177 un.sun_family = AF_UNIX; 178 strncpy (un.sun_path, 179 my_unixpath, 180 sizeof (un.sun_path) - 1); 181 if (0 != bind (sock, 182 (const struct sockaddr *) &un, 183 sizeof (un))) 184 { 185 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 186 "bind", 187 my_unixpath); 188 GNUNET_break (0 == close (sock)); 189 goto cleanup; 190 } 191 ret = GNUNET_NETWORK_socket_box_native (sock); 192 if (GNUNET_OK != 193 GNUNET_NETWORK_socket_listen (ret, 194 512)) 195 { 196 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 197 "listen", 198 my_unixpath); 199 GNUNET_break (GNUNET_OK == 200 GNUNET_NETWORK_socket_close (ret)); 201 ret = NULL; 202 } 203 } 204 cleanup: 205 (void) umask (old_umask); 206 return ret; 207 } 208 209 210 void 211 TES_wake_clients (void) 212 { 213 uint64_t num = 1; 214 215 GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock)); 216 for (struct TES_Client *client = TES_clients_head; 217 NULL != client; 218 client = client->next) 219 { 220 #ifdef __linux__ 221 if (-1 == client->esock) 222 continue; 223 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 GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock)); 237 } 238 239 240 enum GNUNET_GenericReturnValue 241 TES_read_work (void *cls, 242 TES_MessageDispatch dispatch) 243 { 244 struct TES_Client *client = cls; 245 char *buf = client->iobuf; 246 size_t off = 0; 247 uint16_t msize = 0; 248 const struct GNUNET_MessageHeader *hdr = NULL; 249 enum GNUNET_GenericReturnValue ret; 250 251 do 252 { 253 ssize_t recv_size; 254 255 recv_size = recv (client->csock, 256 &buf[off], 257 sizeof (client->iobuf) - off, 258 0); 259 if (-1 == recv_size) 260 { 261 if ( (0 == off) && 262 (EAGAIN == errno) ) 263 return GNUNET_NO; 264 if ( (EINTR == errno) || 265 (EAGAIN == errno) ) 266 { 267 GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, 268 "recv"); 269 continue; 270 } 271 if (ECONNRESET != errno) 272 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 273 "recv"); 274 return GNUNET_SYSERR; 275 } 276 if (0 == recv_size) 277 { 278 /* regular disconnect? */ 279 GNUNET_break_op (0 == off); 280 return GNUNET_SYSERR; 281 } 282 off += recv_size; 283 more: 284 if (off < sizeof (struct GNUNET_MessageHeader)) 285 continue; 286 hdr = (const struct GNUNET_MessageHeader *) buf; 287 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 if (msize < sizeof (struct GNUNET_MessageHeader)) 295 { 296 GNUNET_break_op (0); 297 return GNUNET_SYSERR; 298 } 299 } while (off < msize); 300 301 ret = dispatch (client, 302 hdr); 303 if ( (GNUNET_OK != ret) || 304 (off == msize) ) 305 return ret; 306 memmove (buf, 307 &buf[msize], 308 off - msize); 309 off -= msize; 310 goto more; 311 } 312 313 314 bool 315 TES_await_ready (struct TES_Client *client) 316 { 317 /* wait for reply with 1s timeout */ 318 struct pollfd pfds[] = { 319 { 320 .fd = client->csock, 321 .events = POLLIN 322 }, 323 { 324 #ifdef __linux__ 325 .fd = client->esock, 326 #else 327 .fd = client->esock_out, 328 #endif 329 .events = POLLIN 330 }, 331 }; 332 int ret; 333 334 ret = poll (pfds, 335 2, 336 -1); 337 if ( (-1 == ret) && 338 (EINTR != errno) ) 339 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 340 "poll"); 341 for (int i = 0; i<2; i++) 342 { 343 if ( 344 #ifdef __linux__ 345 (pfds[i].fd == client->esock) && 346 #else 347 (pfds[i].fd == client->esock_out) && 348 #endif 349 (POLLIN == pfds[i].revents) ) 350 { 351 uint64_t num; 352 353 #ifdef __linux__ 354 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 return true; 365 } 366 } 367 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 sign_worker (void *cls) 379 { 380 struct TES_Client *client = cls; 381 382 if (GNUNET_OK != 383 client->cb.init (client)) 384 { 385 GNUNET_break (0); 386 return NULL; 387 } 388 while (! in_shutdown) 389 { 390 if (TES_await_ready (client)) 391 { 392 if (GNUNET_OK != 393 client->cb.updater (client)) 394 break; 395 } 396 else 397 { 398 if (GNUNET_SYSERR == 399 TES_read_work (client, 400 client->cb.dispatch)) 401 break; 402 } 403 } 404 GNUNET_break (0 == close (client->csock)); 405 client->csock = -1; 406 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 join_client (struct TES_Client *pos) 418 { 419 void *rval; 420 421 GNUNET_CONTAINER_DLL_remove (TES_clients_head, 422 TES_clients_tail, 423 pos); 424 GNUNET_break (0 == 425 pthread_join (pos->worker, 426 &rval)); 427 #ifdef __linux__ 428 GNUNET_break (0 == close (pos->esock)); 429 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 GNUNET_free (pos); 437 } 438 439 440 /** 441 * Task that listens for incoming clients. 442 * 443 * @param cls a `struct TES_Callbacks` 444 */ 445 static void 446 listen_job (void *cls) 447 { 448 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 socklen_t sa_len = sizeof (sa); 457 458 listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 459 unix_sock, 460 &listen_job, 461 cls); 462 s = accept (GNUNET_NETWORK_get_fd (unix_sock), 463 (struct sockaddr *) &sa, 464 &sa_len); 465 if (-1 == s) 466 { 467 bool st = ( (ENFILE == errno) || 468 (EMFILE == errno) ); 469 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 470 "accept"); 471 if (st) 472 { 473 GNUNET_SCHEDULER_cancel (listen_task); 474 listen_task = NULL; 475 } 476 return; 477 } 478 #ifdef __linux__ 479 e = eventfd (0, 480 EFD_CLOEXEC); 481 if (-1 == e) 482 { 483 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 484 "eventfd"); 485 GNUNET_break (0 == close (s)); 486 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 client = GNUNET_new (struct TES_Client); 502 client->cb = *cb; 503 client->csock = s; 504 #ifdef __linux__ 505 client->esock = e; 506 #else 507 client->esock_in = e[1]; 508 client->esock_out = e[0]; 509 #endif 510 GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock)); 511 for (struct TES_Client *pos = TES_clients_head; 512 NULL != pos; 513 pos = nxt) 514 { 515 nxt = pos->next; 516 if (-1 == pos->csock) 517 { 518 join_client (pos); 519 } 520 } 521 GNUNET_CONTAINER_DLL_insert (TES_clients_head, 522 TES_clients_tail, 523 client); 524 GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock)); 525 if (0 != 526 pthread_create (&client->worker, 527 NULL, 528 &sign_worker, 529 client)) 530 { 531 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 532 "pthread_create"); 533 GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock)); 534 GNUNET_CONTAINER_DLL_remove (TES_clients_head, 535 TES_clients_tail, 536 client); 537 GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock)); 538 GNUNET_break (0 == close (client->csock)); 539 #ifdef __linux__ 540 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 GNUNET_free (client); 546 } 547 } 548 } 549 550 551 int 552 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 if (GNUNET_OK != 560 GNUNET_CONFIGURATION_get_value_filename (cfg, 561 section, 562 "SM_PRIV_KEY", 563 &pfn)) 564 { 565 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 566 section, 567 "SM_PRIV_KEY"); 568 return EXIT_NOTCONFIGURED; 569 } 570 if (GNUNET_SYSERR == 571 GNUNET_CRYPTO_eddsa_key_from_file (pfn, 572 GNUNET_YES, 573 &TES_smpriv.eddsa_priv)) 574 { 575 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 576 section, 577 "SM_PRIV_KEY", 578 "Could not use file to persist private key"); 579 GNUNET_free (pfn); 580 return EXIT_NOPERMISSION; 581 } 582 GNUNET_free (pfn); 583 GNUNET_CRYPTO_eddsa_key_get_public (&TES_smpriv.eddsa_priv, 584 &TES_smpub.eddsa_pub); 585 } 586 587 if (GNUNET_OK != 588 GNUNET_CONFIGURATION_get_value_filename (cfg, 589 section, 590 "UNIXPATH", 591 &unixpath)) 592 { 593 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 594 section, 595 "UNIXPATH"); 596 return EXIT_NOTCONFIGURED; 597 } 598 GNUNET_assert (NULL != unixpath); 599 unix_sock = TES_open_socket (unixpath); 600 if (NULL == unix_sock) 601 { 602 GNUNET_free (unixpath); 603 GNUNET_break (0); 604 return EXIT_NOPERMISSION; 605 } 606 /* start job to accept incoming requests on 'sock' */ 607 listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 608 unix_sock, 609 &listen_job, 610 (void *) cb); 611 return 0; 612 } 613 614 615 void 616 TES_listen_stop (void) 617 { 618 struct TES_Client *client; 619 620 if (NULL != listen_task) 621 { 622 GNUNET_SCHEDULER_cancel (listen_task); 623 listen_task = NULL; 624 } 625 if (NULL != unix_sock) 626 { 627 GNUNET_break (GNUNET_OK == 628 GNUNET_NETWORK_socket_close (unix_sock)); 629 unix_sock = NULL; 630 } 631 if (0 != unlink (unixpath)) 632 { 633 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 634 "unlink", 635 unixpath); 636 } 637 GNUNET_free (unixpath); 638 in_shutdown = true; 639 TES_wake_clients (); 640 GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock)); 641 while (NULL != (client = TES_clients_head)) 642 join_client (client); 643 GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock)); 644 }