secmod_common.c (16405B)
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 "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 #ifdef __linux__ 408 GNUNET_break (0 == close (client->esock)); 409 client->esock = -1; 410 #else 411 GNUNET_break (0 == close (client->esock_in)); 412 client->esock_in = -1; 413 GNUNET_break (0 == close (client->esock_out)); 414 client->esock_out = -1; 415 #endif 416 return NULL; 417 } 418 419 420 /** 421 * Task that listens for incoming clients. 422 * 423 * @param cls a `struct TES_Callbacks` 424 */ 425 static void 426 listen_job (void *cls) 427 { 428 const struct TES_Callbacks *cb = cls; 429 int s; 430 #ifdef __linux__ 431 int e; 432 #else 433 int e[2]; 434 #endif 435 struct sockaddr_storage sa; 436 socklen_t sa_len = sizeof (sa); 437 438 listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 439 unix_sock, 440 &listen_job, 441 cls); 442 s = accept (GNUNET_NETWORK_get_fd (unix_sock), 443 (struct sockaddr *) &sa, 444 &sa_len); 445 if (-1 == s) 446 { 447 bool st = ( (ENFILE == errno) || 448 (EMFILE == errno) ); 449 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 450 "accept"); 451 if (st) 452 { 453 GNUNET_SCHEDULER_cancel (listen_task); 454 listen_task = NULL; 455 } 456 return; 457 } 458 #ifdef __linux__ 459 e = eventfd (0, 460 EFD_CLOEXEC); 461 if (-1 == e) 462 { 463 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 464 "eventfd"); 465 GNUNET_break (0 == close (s)); 466 return; 467 } 468 #else 469 if (0 != pipe (e)) 470 { 471 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 472 "pipe"); 473 GNUNET_break (0 == close (s)); 474 return; 475 } 476 #endif 477 { 478 struct TES_Client *client; 479 struct TES_Client *nxt; 480 481 client = GNUNET_new (struct TES_Client); 482 client->cb = *cb; 483 client->csock = s; 484 #ifdef __linux__ 485 client->esock = e; 486 #else 487 client->esock_in = e[1]; 488 client->esock_out = e[0]; 489 #endif 490 GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock)); 491 for (struct TES_Client *pos = TES_clients_head; 492 NULL != pos; 493 pos = nxt) 494 { 495 nxt = pos->next; 496 if (-1 == pos->csock) 497 { 498 void *rval; 499 500 GNUNET_CONTAINER_DLL_remove (TES_clients_head, 501 TES_clients_tail, 502 pos); 503 GNUNET_break (0 == 504 pthread_join (pos->worker, 505 &rval)); 506 GNUNET_free (pos); 507 } 508 } 509 GNUNET_CONTAINER_DLL_insert (TES_clients_head, 510 TES_clients_tail, 511 client); 512 GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock)); 513 if (0 != 514 pthread_create (&client->worker, 515 NULL, 516 &sign_worker, 517 client)) 518 { 519 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 520 "pthread_create"); 521 GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock)); 522 GNUNET_CONTAINER_DLL_remove (TES_clients_head, 523 TES_clients_tail, 524 client); 525 GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock)); 526 GNUNET_break (0 == close (client->csock)); 527 #ifdef __linux__ 528 GNUNET_break (0 == close (client->esock)); 529 #else 530 GNUNET_break (0 == close (client->esock_in)); 531 GNUNET_break (0 == close (client->esock_out)); 532 #endif 533 GNUNET_free (client); 534 } 535 } 536 } 537 538 539 int 540 TES_listen_start (const struct GNUNET_CONFIGURATION_Handle *cfg, 541 const char *section, 542 const struct TES_Callbacks *cb) 543 { 544 { 545 char *pfn; 546 547 if (GNUNET_OK != 548 GNUNET_CONFIGURATION_get_value_filename (cfg, 549 section, 550 "SM_PRIV_KEY", 551 &pfn)) 552 { 553 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 554 section, 555 "SM_PRIV_KEY"); 556 return EXIT_NOTCONFIGURED; 557 } 558 if (GNUNET_SYSERR == 559 GNUNET_CRYPTO_eddsa_key_from_file (pfn, 560 GNUNET_YES, 561 &TES_smpriv.eddsa_priv)) 562 { 563 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 564 section, 565 "SM_PRIV_KEY", 566 "Could not use file to persist private key"); 567 GNUNET_free (pfn); 568 return EXIT_NOPERMISSION; 569 } 570 GNUNET_free (pfn); 571 GNUNET_CRYPTO_eddsa_key_get_public (&TES_smpriv.eddsa_priv, 572 &TES_smpub.eddsa_pub); 573 } 574 575 if (GNUNET_OK != 576 GNUNET_CONFIGURATION_get_value_filename (cfg, 577 section, 578 "UNIXPATH", 579 &unixpath)) 580 { 581 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 582 section, 583 "UNIXPATH"); 584 return EXIT_NOTCONFIGURED; 585 } 586 GNUNET_assert (NULL != unixpath); 587 unix_sock = TES_open_socket (unixpath); 588 if (NULL == unix_sock) 589 { 590 GNUNET_free (unixpath); 591 GNUNET_break (0); 592 return EXIT_NOPERMISSION; 593 } 594 /* start job to accept incoming requests on 'sock' */ 595 listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 596 unix_sock, 597 &listen_job, 598 (void *) cb); 599 return 0; 600 } 601 602 603 void 604 TES_listen_stop (void) 605 { 606 struct TES_Client *client; 607 608 if (NULL != listen_task) 609 { 610 GNUNET_SCHEDULER_cancel (listen_task); 611 listen_task = NULL; 612 } 613 if (NULL != unix_sock) 614 { 615 GNUNET_break (GNUNET_OK == 616 GNUNET_NETWORK_socket_close (unix_sock)); 617 unix_sock = NULL; 618 } 619 if (0 != unlink (unixpath)) 620 { 621 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 622 "unlink", 623 unixpath); 624 } 625 GNUNET_free (unixpath); 626 in_shutdown = true; 627 TES_wake_clients (); 628 GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock)); 629 while (NULL != (client = TES_clients_head)) 630 { 631 void *rval; 632 633 GNUNET_CONTAINER_DLL_remove (TES_clients_head, 634 TES_clients_tail, 635 client); 636 GNUNET_break (0 == 637 pthread_join (client->worker, 638 &rval)); 639 GNUNET_free (client); 640 } 641 GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock)); 642 }