/src/pdsh/mod.c
C | 1100 lines | 726 code | 221 blank | 153 comment | 174 complexity | 3a1282cef33a63ce5a75b60a915dfd24 MD5 | raw file
1/*****************************************************************************\ 2 * $Id$ 3 ***************************************************************************** 4 * Copyright (C) 2001-2006 The Regents of the University of California. 5 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). 6 * Written by Jim Garlick <garlick@llnl.gov>. 7 * UCRL-CODE-2003-005. 8 * 9 * This file is part of Pdsh, a parallel remote shell program. 10 * For details, see <http://www.llnl.gov/linux/pdsh/>. 11 * 12 * Pdsh is free software; you can redistribute it and/or modify it under 13 * the terms of the GNU General Public License as published by the Free 14 * Software Foundation; either version 2 of the License, or (at your option) 15 * any later version. 16 * 17 * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY 18 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 19 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 20 * details. 21 * 22 * You should have received a copy of the GNU General Public License along 23 * with Pdsh; if not, write to the Free Software Foundation, Inc., 24 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 25\*****************************************************************************/ 26#if HAVE_CONFIG_H 27# include <config.h> 28#endif 29#if HAVE_FEATURES_H 30# include <features.h> 31#endif 32 33#include <sys/param.h> 34#include <sys/types.h> 35#include <sys/stat.h> 36#include <unistd.h> 37#include <dirent.h> 38#include <assert.h> 39#include <string.h> 40 41#if STATIC_MODULES 42#include "static_modules.h" 43#else 44#include "ltdl.h" 45#endif 46 47#include "src/common/err.h" 48#include "src/common/xmalloc.h" 49#include "src/common/xstring.h" 50#include "src/common/hostlist.h" 51#include "src/common/list.h" 52#include "src/common/split.h" 53#include "mod.h" 54 55/* 56 * pdsh/322: Workaround apparent bug in glibc 2.2.4 which 57 * occaisionally causes LinuxThreads manager thread to 58 * segfault at exit. (Disable dlclose() and lt_dlexit() 59 * in these versions of glibc) 60 */ 61#if (__GLIBC__ == 2) && (__GLIBC_MINOR__ < 3) 62# define PREVENT_DLCLOSE_BUG 1 63#endif 64 65/* 66 * Components of a module. 67 */ 68struct module_components { 69#ifndef NDEBUG 70#define MOD_MAGIC 0xc0c0b0b 71 int magic; 72#endif 73 74#if !STATIC_MODULES 75 lt_dlhandle handle; 76#endif 77 char *filename; 78 79 int priority; 80 int initialized; 81 82 struct pdsh_module *pmod; 83}; 84 85 86typedef enum permission_error { 87 DIR_OK, 88 DIR_NOT_DIRECTORY, 89 DIR_BAD_OWNER, 90 DIR_WORLD_WRITABLE 91} perm_error_t; 92 93 94/* 95 * Static function prototypes: 96 */ 97#if STATIC_MODULES 98static int _mod_load_static_modules(void); 99static int _mod_load_static(int); 100#else 101static int _mod_load_dynamic_modules(const char *, opt_t *); 102static int _mod_load_dynamic(const char *); 103static int _cmp_filenames(mod_t, char *); 104static int _is_loaded(char *filename); 105static bool _path_permissions_ok(const char *dir, uid_t pdsh_owner); 106static perm_error_t _dir_permission_error(struct stat *, uid_t alt_uid); 107#endif 108static int _mod_initialize(mod_t mod); 109static int _mod_init_list_safe(mod_t mod, void *arg); 110static void _mod_destroy(mod_t mod); 111static bool _mod_opts_ok(mod_t mod); 112static int _mod_print_info(mod_t mod); 113static void _print_option_help(struct pdsh_module_option *p, int col); 114static struct pdsh_module_option * _mod_find_opt(mod_t mod, int opt); 115 116/* 117 * Static list of loaded modules 118 */ 119static List module_list; 120static bool initialized = false; 121 122int 123mod_init(void) 124{ 125 if (!initialized) { 126 if (!(module_list = list_create((ListDelF) _mod_destroy))) { 127 err("Unable to create module list\n"); 128 return -1; 129 } 130 initialized = true; 131#if STATIC_MODULES 132 return 0; 133#else 134 return lt_dlinit(); 135#endif 136 } else 137 return 0; 138} 139 140int 141mod_exit(void) 142{ 143 if (!initialized) 144 return 0; 145 146 /* 147 * list_destroy() will call module destructor on each 148 * element in list 149 */ 150 list_destroy(module_list); 151 152#if STATIC_MODULES || PREVENT_DLCLOSE_BUG 153 return 0; 154#else 155 return lt_dlexit(); 156#endif 157} 158 159hostlist_t 160_mod_read_wcoll(mod_t mod, opt_t *pdsh_opts) 161{ 162 if (mod->pmod->mod_ops && mod->pmod->mod_ops->read_wcoll) 163 return (*mod->pmod->mod_ops->read_wcoll) (pdsh_opts); 164 return 0; 165} 166 167 168int 169_mod_postop(mod_t mod, opt_t *pdsh_opts) 170{ 171 if (mod->pmod->mod_ops && mod->pmod->mod_ops->postop) 172 return (*mod->pmod->mod_ops->postop) (pdsh_opts); 173 174 return 0; 175} 176 177 178/* 179 * Like list_next (i), but skip over inactive modules 180 */ 181static mod_t _mod_next_active (ListIterator i) 182{ 183 mod_t mod; 184 185 while ((mod = list_next (i))) { 186 if (mod->initialized) 187 return (mod); 188 } 189 190 return (NULL); 191} 192 193 194/* 195 * Call any "read wcoll" functions exported by modules. The module 196 * is responsible for deciding when to generate a new wcoll, 197 * append to existing wcoll, etc. (presumably based on the contents 198 * of opt) 199 * 200 * This function appends to wcoll any new hosts returned from the 201 * module specific read_wcoll() functions. 202 * 203 * Returns -1 on error, 0 for success. 204 */ 205int 206mod_read_wcoll(opt_t *opt) 207{ 208 mod_t mod; 209 ListIterator module_itr; 210 211 if (!initialized) 212 mod_init(); 213 214 if (!(module_itr = list_iterator_create(module_list))) { 215 err("Unable to create module list iterator\n"); 216 return -1; 217 } 218 219 while ((mod = _mod_next_active (module_itr))) { 220 hostlist_t hl = NULL; 221 222 if (!(hl = _mod_read_wcoll(mod, opt))) 223 continue; 224 225 if (opt->wcoll != NULL) { 226 hostlist_push_list(opt->wcoll, hl); 227 hostlist_destroy(hl); 228 } else 229 opt->wcoll = hl; 230 } 231 232 list_iterator_destroy(module_itr); 233 234 return 0; 235} 236 237int 238mod_postop(opt_t *pdsh_opts) 239{ 240 mod_t mod; 241 int errors = 0; 242 ListIterator module_itr; 243 244 if (!initialized) 245 mod_init(); 246 247 if (!(module_itr = list_iterator_create(module_list))) { 248 err("Unable to create module list iterator\n"); 249 return 1; 250 } 251 252 while ((mod = _mod_next_active (module_itr))) 253 errors += _mod_postop(mod, pdsh_opts); 254 255 list_iterator_destroy(module_itr); 256 257 return errors; 258} 259 260 261mod_t 262mod_create(void) 263{ 264 mod_t mod = Malloc(sizeof(*mod)); 265#if !STATIC_MODULES 266 mod->handle = NULL; 267#endif 268 mod->filename = NULL; 269 270 mod->priority = DEFAULT_MODULE_PRIORITY; 271 mod->initialized = 0; 272 assert(mod->magic = MOD_MAGIC); 273 return mod; 274} 275 276static void 277_mod_destroy(mod_t mod) 278{ 279 assert(mod->magic == MOD_MAGIC); 280 281 /* must assert mod->mod, loading of module may have failed */ 282 if (mod->pmod) { 283 mod->pmod->type = NULL; 284 mod->pmod->name = NULL; 285 286 /* 287 * Only run exit function if module was initialized 288 */ 289 if (mod->initialized && 290 mod->pmod->mod_ops && mod->pmod->mod_ops->exit) 291 (*mod->pmod->mod_ops->exit)(); 292 } 293 294 if (mod->filename) 295 Free((void **) &mod->filename); 296 297#if !STATIC_MODULES 298# if !PREVENT_DLCLOSE_BUG 299 if (mod->handle) 300 lt_dlclose(mod->handle); 301# endif 302#endif 303 304 assert(mod->magic = ~MOD_MAGIC); 305 Free((void **) &mod); 306 307 return; 308} 309 310static bool 311_mod_opts_ok(mod_t mod) 312{ 313 if (!opt_register(mod->pmod->opt_table)) 314 return false; 315 316 return true; 317} 318 319static int 320_cmp_f (mod_t x, mod_t y) 321{ 322 if (x->priority == y->priority) 323 return strcmp (x->pmod->name, y->pmod->name); 324 return (y->priority - x->priority); 325} 326 327 328static int 329_mod_find_misc (mod_t mod, const char *name) 330{ 331 if (strcmp (mod->pmod->type, "misc") != 0) 332 return 0; 333 if (strcmp (mod->pmod->name, name) != 0) 334 return 0; 335 return 1; 336} 337 338static int 339_mod_initialize_by_name (char *name, List l) 340{ 341 mod_t mod = list_find_first (l, (ListFindF) _mod_find_misc, name); 342 if (mod != NULL && _mod_initialize (mod) < 0) 343 err("%p: Warning: Failed to initialize requested module \"%s/%s\"\n", 344 mod->pmod->type, mod->pmod->name); 345 return (0); 346} 347 348 349static int _mod_initialize_modules_by_name (char *names, List m) 350{ 351 List l; 352 353 if (names == NULL) 354 return (0); 355 356 l = list_split (",", names); 357 list_for_each (l, (ListForF) _mod_initialize_by_name, m); 358 return (0); 359} 360 361int mod_load_modules(const char *dir, opt_t *opt) 362{ 363 int rc = 0; 364#if STATIC_MODULES 365 rc = _mod_load_static_modules(); 366#else 367 rc = _mod_load_dynamic_modules(dir, opt); 368#endif 369 370 list_sort(module_list, (ListCmpF) _cmp_f); 371 372 /* 373 * Initialize misc modules by name 374 */ 375 _mod_initialize_modules_by_name (opt->misc_modules, module_list); 376 377 /* 378 * Initialize remaining modules in modules_list: 379 */ 380 list_for_each (module_list, (ListForF) _mod_init_list_safe, NULL); 381 382 return(rc); 383} 384 385 386/* 387 * Print all options from module option table 'p,' aligning description 388 * with column 'col' 389 */ 390static void 391_print_option_help(struct pdsh_module_option *p, int col) 392{ 393 char buf[81]; 394 395 assert(p != NULL); 396 397 snprintf(buf, 81, "-%c %-*s %s\n", p->opt, col - 4, 398 (p->arginfo ? p->arginfo : ""), p->descr); 399 400 err("%s", buf); 401} 402 403void 404mod_print_options(mod_t mod, int col) 405{ 406 struct pdsh_module_option *p; 407 408 assert(mod != NULL); 409 assert(mod->pmod != NULL); 410 411 p = mod->pmod->opt_table; 412 if (!p || !p->opt) 413 return; 414 /* 415 * out("%s/%s Options:\n", mod->pmod->type, mod->pmod->name); 416 */ 417 for (p = mod->pmod->opt_table; p && (p->opt != 0); p++) 418 _print_option_help(p, col); 419 420} 421 422/* 423 * Print to stdout information stanza for module "mod" 424 */ 425static int 426_mod_print_info(mod_t mod) 427{ 428 if (mod == NULL) 429 return 0; 430 431 out("Module: %s/%s\n", mod->pmod->type, mod->pmod->name); 432 out("Author: %s\n", mod->pmod->author ? mod->pmod->author : "???"); 433 out("Descr: %s\n", mod->pmod->descr ? mod->pmod->descr : "???"); 434 out("Active: %s\n", mod->initialized ? "yes" : "no"); 435 436 if (mod->pmod->opt_table && mod->pmod->opt_table->opt) { 437 out("Options:\n"); 438 mod_print_options(mod, 18); 439 } 440 441 out("\n"); 442 443 return 0; 444} 445 446static int _opt_print(mod_t mod, int *col) 447{ 448 if (mod->initialized) 449 mod_print_options(mod, *col); 450 return 0; 451} 452 453 454void mod_print_all_options(int col) 455{ 456 list_for_each(module_list, (ListForF) _opt_print, &col); 457} 458 459 460static int 461_cmp_type(mod_t mod, char *type) 462{ 463 return (strcmp(mod->pmod->type, type) == 0); 464} 465 466int 467mod_count(char *type) 468{ 469 int i = 0; 470 ListIterator module_itr; 471 472 assert(module_list != NULL); 473 474 if (type == NULL) 475 return list_count(module_list); 476 477 if (!(module_itr = list_iterator_create(module_list))) { 478 err("Unable to create module list iterator\n"); 479 return -1; 480 } 481 482 while (list_find(module_itr, (ListFindF) _cmp_type, type)) 483 i++; 484 485 list_iterator_destroy(module_itr); 486 487 return i; 488} 489 490static List 491_mod_get_module_names(char *type, int get_active) 492{ 493 List l; 494 mod_t mod; 495 ListIterator module_itr; 496 497 assert(module_list != NULL); 498 499 l = list_create(NULL); 500 501 if (!(module_itr = list_iterator_create(module_list))) { 502 err("Unable to create module list iterator\n"); 503 list_destroy(l); 504 return NULL; 505 } 506 507 while ((mod = (type ? list_find(module_itr, (ListFindF) _cmp_type, type) 508 : list_next(module_itr)))) { 509 /* 510 * Push active (initialized) modules onto list if get_active 511 * is true, otherwise push inactive (!initialized) modules: 512 */ 513 if (!get_active == !mod->initialized) 514 list_push(l, mod->pmod->name); 515 } 516 517 list_iterator_destroy(module_itr); 518 519 return l; 520} 521 522List mod_get_module_names (char *type) 523{ 524 return _mod_get_module_names (type, 1); 525} 526 527List mod_get_uninitialized_module_names (char *type) 528{ 529 return _mod_get_module_names (type, 0); 530} 531 532 533void 534mod_list_module_info(void) 535{ 536 int nmodules = list_count(module_list); 537 out("%d module%s loaded:\n\n", nmodules, (nmodules > 1 ? "s" : "")); 538 539 if (nmodules == 0) 540 return; 541 542 list_for_each(module_list, (ListForF) _mod_print_info, NULL); 543} 544 545 546static int 547_mod_description_match (mod_t m, const char *type, const char *name) 548{ 549 if ( (strcmp(m->pmod->type, type) == 0) 550 && (strcmp(m->pmod->name, name) == 0) ) 551 return (1); 552 return (0); 553} 554 555 556mod_t 557mod_get_module(const char *type, const char *name) 558{ 559 mod_t mod; 560 ListIterator module_itr; 561 562 assert(type != NULL); 563 assert(name != NULL); 564 565 if (!(module_itr = list_iterator_create(module_list))) { 566 err("Unable to create module list iterator\n"); 567 return NULL; 568 } 569 570 while ((mod = list_next(module_itr))) { 571 if (_mod_description_match (mod, type, name)) 572 break; 573 } 574 575 list_iterator_destroy(module_itr); 576 577 return mod; 578} 579 580char * 581mod_get_name(mod_t mod) 582{ 583 assert(mod != NULL); 584 assert(mod->pmod != NULL); 585 586 return mod->pmod->name; 587} 588 589char * 590mod_get_type(mod_t mod) 591{ 592 assert(mod != NULL); 593 assert(mod->pmod != NULL); 594 595 return mod->pmod->type; 596} 597 598RcmdInitF 599mod_get_rcmd_init(mod_t mod) { 600 601 assert(mod != NULL); 602 assert(mod->pmod != NULL); 603 604 if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd_init) 605 return mod->pmod->rcmd_ops->rcmd_init; 606 else 607 return NULL; 608} 609 610RcmdSigF 611mod_get_rcmd_signal(mod_t mod) { 612 613 assert(mod != NULL); 614 assert(mod->pmod != NULL); 615 616 if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd_signal) 617 return mod->pmod->rcmd_ops->rcmd_signal; 618 else 619 return NULL; 620} 621 622RcmdF 623mod_get_rcmd(mod_t mod) { 624 625 assert(mod != NULL); 626 assert(mod->pmod != NULL); 627 628 if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd) 629 return mod->pmod->rcmd_ops->rcmd; 630 else 631 return NULL; 632} 633 634RcmdDestroyF 635mod_get_rcmd_destroy (mod_t mod) 636{ 637 assert (mod != NULL); 638 assert (mod->pmod != NULL); 639 640 if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd_destroy) 641 return mod->pmod->rcmd_ops->rcmd_destroy; 642 else 643 return NULL; 644} 645 646 647int 648mod_process_opt(opt_t *opt, int c, char *optarg) 649{ 650 mod_t mod; 651 struct pdsh_module_option *p = NULL; 652 ListIterator module_itr; 653 654 if (!(module_itr = list_iterator_create(module_list))) { 655 err("Unable to create module list iterator\n"); 656 return -1; 657 } 658 659 while ((mod = _mod_next_active (module_itr))) { 660 if ((p = _mod_find_opt(mod, c))) { 661 list_iterator_destroy(module_itr); 662 return p->f(opt, c, optarg); 663 } 664 } 665 666 list_iterator_destroy(module_itr); 667 668 return -1; 669} 670 671 672/* 673 * Return pointer to pdsh_module_option struct for option `opt' 674 * or NULL if no loaded module provides that option. 675 */ 676static struct pdsh_module_option * 677_mod_find_opt(mod_t mod, int opt) 678{ 679 struct pdsh_module_option *p = mod->pmod->opt_table; 680 for (p = mod->pmod->opt_table; p && (p->opt != 0); p++) 681 if (p->opt == opt) return p; 682 return NULL; 683} 684 685static int 686_mod_delete (const char *type, const char *name) 687{ 688 ListIterator i = list_iterator_create (module_list); 689 mod_t m; 690 691 while ((m = list_next (i))) { 692 if (_mod_description_match (m, type, name)) 693 list_delete (i); 694 } 695 return (0); 696} 697 698 699static int _mod_register (mod_t mod, const char *name) 700{ 701 mod_t prev; 702 /* 703 * Must have atleast a name and type 704 */ 705 if (!mod->pmod->type || !mod->pmod->name) { 706 err("%p:[%s] type or name not specified in module\n", name); 707 return -1; 708 } 709 710 /* 711 * Check for existing module of the same type and name 712 * Delete previous module if its priority is higher. 713 */ 714 if ((prev = mod_get_module (mod->pmod->type, mod->pmod->name))) { 715 err("%p: %s: [%s/%s] already loaded from [%s]\n", 716 mod->filename, mod->pmod->type, mod->pmod->name, 717 prev->filename); 718 if (mod->priority > prev->priority) 719 _mod_delete (mod->pmod->type, mod->pmod->name); 720 else 721 return (-1); 722 } 723 724 /* 725 * Continue with module loading only if personality acceptable 726 */ 727 if (!(mod->pmod->personality & pdsh_personality())) 728 return -1; 729 730 list_prepend(module_list, mod); 731 732 return 0; 733} 734 735static int _mod_initialize (mod_t mod) 736{ 737 if (!_mod_opts_ok(mod)) 738 return -1; 739 740 if (mod->pmod->mod_ops && 741 mod->pmod->mod_ops->init && 742 ((*mod->pmod->mod_ops->init)() < 0)) { 743 err("%p: error: %s/%s failed to initialize.\n", 744 mod->pmod->type, mod->pmod->name); 745 return -1; 746 } 747 748 mod->initialized = 1; 749 750 return 0; 751} 752 753/* 754 * Version of _mod_initialize that always returns zero 755 * for use with list_for_each. 756 */ 757static int _mod_init_list_safe (mod_t mod, void *arg) 758{ 759 _mod_initialize (mod); 760 return 0; 761} 762 763 764#if STATIC_MODULES 765/* 766 * Set pdsh module pointer (pmod) to point to address from 767 * statically defined external static_mods array. 768 */ 769static int 770_mod_load_static(int idx) 771{ 772 mod_t mod = mod_create(); 773 774 mod->pmod = static_mods[idx]; 775 mod->priority = *priority[idx]; 776 mod->filename = Strdup("static"); 777 778 _mod_register(mod, static_mod_names[idx]); 779 780 return 0; 781} 782 783/* 784 * Load all statically defined modules from internal static_mods array 785 */ 786static int 787_mod_load_static_modules(void) 788{ 789 int i = 0; 790 791 while (static_mods[i] != NULL) { 792 if (_mod_load_static(i++) < 0) 793 continue; 794 } 795 796 return 0; 797} 798 799 800#else /* !STATIC_MODULES */ 801 802/* 803 * Load a single module from file `fq_path' and append to module_list. 804 */ 805static int 806_mod_load_dynamic(const char *fq_path) 807{ 808 mod_t mod = NULL; 809 const lt_dlinfo *info; 810 int *priority; 811 assert(fq_path != NULL); 812 813 mod = mod_create(); 814 815 if (!(mod->handle = lt_dlopen(fq_path))) 816 goto fail; 817 818 if (!(info = lt_dlgetinfo(mod->handle))) 819 goto fail_libtool_broken; 820 821 if (info->filename == NULL) 822 goto fail_libtool_broken; 823 824 mod->filename = Strdup(info->filename); 825 826 if (_is_loaded(mod->filename)) { 827 /* Module already loaded. This is OK, no need for 828 * error message. (Could have already opened a .la and 829 * we are now opening the corresponding .so 830 */ 831 goto fail; 832 } 833 834 /* load all module info from the pdsh_module structure */ 835 if (!(mod->pmod = lt_dlsym(mod->handle, "pdsh_module_info"))) { 836 err("%p:[%s] can't resolve pdsh module\n", mod->filename); 837 goto fail; 838 } 839 840 if ((priority = lt_dlsym(mod->handle, "pdsh_module_priority"))) 841 mod->priority = *priority; 842 843 if (_mod_register(mod, mod->filename) < 0) 844 goto fail; 845 846 return 0; 847 848 fail_libtool_broken: 849 /* 850 * Avoid dlclose() of invalid handle 851 */ 852 mod->handle = NULL; 853 854 fail: 855 _mod_destroy(mod); 856 return -1; 857} 858 859static int 860_pdsh_owner(const char *pdsh_path, uid_t *pdsh_uid) 861{ 862 struct stat st; 863 864 if (stat (pdsh_path, &st) < 0) { 865 err ("%p: Unable to determine ownership of pdsh binary: %m\n"); 866 return -1; 867 } 868 869 *pdsh_uid = st.st_uid; 870 871 return 0; 872} 873 874static int 875_mod_load_dynamic_modules(const char *dir, opt_t *pdsh_opts) 876{ 877 DIR *dirp = NULL; 878 struct dirent *entry = NULL; 879 char path[MAXPATHLEN + 1]; 880 char *p; 881 int count = 0; 882 uid_t pdsh_owner = 0; 883 884 assert(dir != NULL); 885 assert(*dir != '\0'); 886 887 if (!initialized) 888 mod_init(); 889 890 if (_pdsh_owner(pdsh_opts->local_program_path, &pdsh_owner) < 0) 891 return -1; 892 893 if (!_path_permissions_ok(dir, pdsh_owner)) 894 return -1; 895 896 if (!(dirp = opendir(dir))) 897 return -1; 898 899 strncpy(path, dir, MAXPATHLEN); 900 p = path + strlen(dir); 901 *(p++) = '/'; 902 903 while ((entry = readdir(dirp))) { 904 struct stat st; 905 906 strcpy(p, entry->d_name); 907 908 if (stat(path, &st) < 0) 909 continue; 910 if (!S_ISREG(st.st_mode)) 911 continue; 912 913 /* 914 * Do not load modules that could have been altered by 915 * a user other than root or the current user or the user 916 * owning the pdsh executable. Otherwise pdsh could execute 917 * arbitrary code. 918 */ 919 if ( (st.st_uid != 0) && (st.st_uid != getuid()) 920 && (st.st_uid != pdsh_owner)) { 921 err ("%p: skipping insecure module \"%s\" (check owner)\n", path); 922 continue; 923 } 924 if (st.st_mode & S_IWOTH) { 925 err ("%p: skipping insecure module \"%s\" (check perms)\n", path); 926 continue; 927 } 928 929 if (_mod_load_dynamic(path) < 0) 930 continue; 931 932 count++; 933 } 934 935 if (closedir(dirp) < 0) 936 err("%p: error closing %s: %m", dir); 937 938 if (count == 0) 939 errx("%p: no modules found\n"); 940 941 return 0; 942} 943 944 945static int _cmp_filenames(mod_t mod, char *filename) 946{ 947 return (strcmp(mod->filename, filename) == 0); 948} 949 950static int 951_is_loaded(char *filename) 952{ 953 if (list_find_first(module_list, (ListFindF) _cmp_filenames, filename)) 954 return 1; 955 956 return 0; 957} 958 959static char * 960_perm_error_string (perm_error_t error) 961{ 962 switch (error) { 963 case DIR_OK: 964 return ("Permissions are valid"); 965 case DIR_NOT_DIRECTORY: 966 return ("Not a directory"); 967 case DIR_BAD_OWNER: 968 return ("Owner not root, current uid, or pdsh executable owner"); 969 case DIR_WORLD_WRITABLE: 970 return ("World writable and sticky bit is not set"); 971 default: 972 break; 973 } 974 975 return ("Unspecified error"); 976} 977 978 979/* 980 * Return permissions error if stat buffer shows any of the below 981 * 1. This is not a directory. 982 * 2. Ownership something other than root, the current uid, or the 983 * same ownership as the pdsh executable. 984 * 3. Permissions are world writable and sticky bit is not set. 985 */ 986static perm_error_t 987_dir_permission_error(struct stat *st, uid_t alt_uid) 988{ 989 if (!S_ISDIR(st->st_mode)) 990 return DIR_NOT_DIRECTORY; 991 if ( (st->st_uid != 0) && (st->st_uid != getuid()) 992 && (st->st_uid != alt_uid)) 993 return DIR_BAD_OWNER; 994 if ((st->st_mode & S_IWOTH) && !(st->st_mode & S_ISVTX)) 995 return DIR_WORLD_WRITABLE; 996 return DIR_OK; 997} 998 999/* 1000 * Temprarily chdir() to path and use getcwd to return real patch 1001 * to caller. 1002 */ 1003static char * 1004_get_dir_name (const char *path, char *buf, size_t len) 1005{ 1006 int pathlen = 256; 1007 char * orig_path = Malloc (pathlen * sizeof (char)); 1008 1009 while (!getcwd (orig_path, pathlen) && (pathlen < MAXPATHLEN*2)) 1010 Realloc ((void **) &orig_path, pathlen*=2 * sizeof (char)); 1011 1012 if (chdir (path) < 0) 1013 errx ("Unable to chdir() to %s: %m", path); 1014 1015 if (!getcwd (buf, len)) 1016 errx ("Unable to get working directory for module path: %s\n", 1017 path); 1018 1019 if (chdir (orig_path) < 0) 1020 err ("Unable to return to original working directory: %s: %m\n", 1021 orig_path); 1022 1023 Free ((void **) &orig_path); 1024 1025 return (buf); 1026} 1027 1028/* 1029 * Returns true if, for the directory "dir" and all of its parent 1030 * directories, the following are true: 1031 * - ownership is root or the calling user (as returned by getuid()) 1032 * or same ownership as the pdsh or pdcp binary. 1033 * - directory has user write permission only 1034 * 1035 * Returns false if one of the assertions above are false for any 1036 * directory in the tree. 1037 * 1038 */ 1039static bool 1040_path_permissions_ok(const char *dir, uid_t pdsh_owner) 1041{ 1042 struct stat st; 1043 char dirbuf[MAXPATHLEN + 1]; 1044 dev_t rootdev; 1045 ino_t rootino; 1046 perm_error_t error; 1047 int pos = 0; 1048 1049 assert(dir != NULL); 1050 1051 if (stat("/", &st) < 0) { 1052 err("%p: Can't stat root directory: %m\n"); 1053 return false; 1054 } 1055 1056 rootdev = st.st_dev; 1057 rootino = st.st_ino; 1058 1059 strncpy(dirbuf, dir, MAXPATHLEN); 1060 dirbuf[MAXPATHLEN] = '\0'; 1061 pos = strlen(dirbuf); 1062 1063 do { 1064 1065 if (stat(dirbuf, &st) < 0) { 1066 err("%p: Can't stat \"%s\": %m\n", dir); 1067 return false; 1068 } 1069 1070 if ((error = _dir_permission_error(&st, pdsh_owner)) != DIR_OK) { 1071 char buf [MAXPATHLEN]; 1072 err("%p: module path \"%s\" insecure.\n", dir); 1073 err("%p: \"%s\": %s\n", 1074 _get_dir_name (dirbuf, buf, MAXPATHLEN), 1075 _perm_error_string (error)); 1076 return false; 1077 } 1078 1079 /* Check for impending overflow 1080 */ 1081 if (pos > MAXPATHLEN - 3) { 1082 err("%p :-( Path too long while checking permissions\n"); 1083 return false; 1084 } 1085 1086 /* Check parent 1087 */ 1088 strncpy(&dirbuf[pos], "/..", 4); 1089 pos+=3; 1090 1091 } while ( !((st.st_ino == rootino) && (st.st_dev == rootdev)) ); 1092 1093 return true; 1094} 1095 1096#endif /* !STATIC_MODULES */ 1097 1098/* 1099 * vi:tabstop=4 shiftwidth=4 expandtab 1100 */