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