1/* xqtsub.c 2 System dependent functions used only by uuxqt. 3 4 Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor 5 6 This file is part of the Taylor UUCP package. 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License as 10 published by the Free Software Foundation; either version 2 of the 11 License, or (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, but 14 WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 21 22 The author of the program may be contacted at ian@airs.com. 23 */ 24 25#include "uucp.h" 26 27#if USE_RCS_ID 28const char xqtsub_rcsid[] = "$Id: xqtsub.c,v 1.24 2002/03/05 19:10:42 ian Rel $"; 29#endif 30 31#include "uudefs.h" 32#include "uuconf.h" 33#include "system.h" 34#include "sysdep.h" 35 36#include <ctype.h> 37#include <errno.h> 38 39#if HAVE_FCNTL_H 40#include <fcntl.h> 41#else 42#if HAVE_SYS_FILE_H 43#include <sys/file.h> 44#endif 45#endif 46 47#ifndef O_RDONLY 48#define O_RDONLY 0 49#define O_WRONLY 1 50#define O_RDWR 2 51#endif 52 53#ifndef O_NOCTTY 54#define O_NOCTTY 0 55#endif 56 57#ifndef FD_CLOEXEC 58#define FD_CLOEXEC 1 59#endif 60 61#if HAVE_OPENDIR 62#if HAVE_DIRENT_H 63#include <dirent.h> 64#else /* ! HAVE_DIRENT_H */ 65#include <sys/dir.h> 66#define dirent direct 67#endif /* ! HAVE_DIRENT_H */ 68#endif /* HAVE_OPENDIR */ 69 70/* Get a value for EX_TEMPFAIL. */ 71 72#if HAVE_SYSEXITS_H 73#include <sysexits.h> 74#endif 75 76#ifndef EX_TEMPFAIL 77#define EX_TEMPFAIL 75 78#endif 79 80static boolean fclean_uuxqt_dir P((const char *zxqtdir)); 81 82/* Get the full pathname of the command to execute, given the list of 83 permitted commands and the allowed path. */ 84 85char * 86zsysdep_find_command (zcmd, pzcmds, pzpath, pferr) 87 const char *zcmd; 88 char **pzcmds; 89 char **pzpath; 90 boolean *pferr; 91{ 92 char **pz; 93 struct stat s; 94 95 *pferr = FALSE; 96 97 for (pz = pzcmds; *pz != NULL; pz++) 98 { 99 char *zslash; 100 101 if (strcmp (*pz, "ALL") == 0) 102 break; 103 104 zslash = strrchr (*pz, '/'); 105 if (zslash != NULL) 106 ++zslash; 107 else 108 zslash = *pz; 109 if (strcmp (zslash, zcmd) == 0 110 || strcmp (*pz, zcmd) == 0) 111 { 112 /* If we already have an absolute path, we can get out 113 immediately. */ 114 if (**pz == '/') 115 { 116 /* Quick error check. */ 117 if (stat (*pz, &s) != 0) 118 { 119 ulog (LOG_ERROR, "%s: %s", *pz, strerror (errno)); 120 *pferr = TRUE; 121 return NULL; 122 } 123 return zbufcpy (*pz); 124 } 125 break; 126 } 127 } 128 129 /* If we didn't find this command, get out. */ 130 if (*pz == NULL) 131 return NULL; 132 133 /* We didn't find an absolute pathname, so we must look through 134 the path. */ 135 for (pz = pzpath; *pz != NULL; pz++) 136 { 137 char *zname; 138 139 zname = zsysdep_in_dir (*pz, zcmd); 140 if (stat (zname, &s) == 0) 141 return zname; 142 } 143 144 return NULL; 145} 146 147/* Expand a local filename for uuxqt. This is special because uuxqt 148 only wants to expand filenames that start with ~ (it does not want 149 to prepend the current directory to other names) and if the ~ is 150 double, it is turned into a single ~. This returns NULL to 151 indicate that no change was required; it has no way to return 152 error. */ 153 154char * 155zsysdep_xqt_local_file (qsys, zfile) 156 const struct uuconf_system *qsys; 157 const char *zfile; 158{ 159 if (*zfile != '~') 160 return NULL; 161 if (zfile[1] == '~') 162 { 163 size_t clen; 164 char *zret; 165 166 clen = strlen (zfile); 167 zret = zbufalc (clen); 168 memcpy (zret, zfile + 1, clen); 169 return zret; 170 } 171 return zsysdep_local_file (zfile, qsys->uuconf_zpubdir, 172 (boolean *) NULL); 173} 174 175#if ! ALLOW_FILENAME_ARGUMENTS 176 177/* Check to see whether an argument specifies a file name; if it does, 178 make sure that the file may legally be sent and/or received. For 179 Unix, we do not permit any occurrence of "/../" in the name, nor 180 may it start with "../". Otherwise, if it starts with "/" we check 181 against the list of permitted files. */ 182 183boolean 184fsysdep_xqt_check_file (qsys, zfile) 185 const struct uuconf_system *qsys; 186 const char *zfile; 187{ 188 size_t clen; 189 190 /* Disallow exact "..", prefix "../", suffix "/..", internal "/../", 191 and restricted absolute paths. */ 192 clen = strlen (zfile); 193 if ((clen == sizeof ".." - 1 194 && strcmp (zfile, "..") == 0) 195 || strncmp (zfile, "../", sizeof "../" - 1) == 0 196 || (clen >= sizeof "/.." - 1 197 && strcmp (zfile + clen - (sizeof "/.." - 1), "/..") == 0) 198 || strstr (zfile, "/../") != NULL 199 || (*zfile == '/' 200 && (! fin_directory_list (zfile, qsys->uuconf_pzremote_send, 201 qsys->uuconf_zpubdir, TRUE, FALSE, 202 (const char *) NULL) 203 || ! fin_directory_list (zfile, qsys->uuconf_pzremote_receive, 204 qsys->uuconf_zpubdir, TRUE, FALSE, 205 (const char *) NULL)))) 206 { 207 ulog (LOG_ERROR, "Not permitted to refer to file \"%s\"", zfile); 208 return FALSE; 209 } 210 211 return TRUE; 212} 213 214#endif /* ! ALLOW_FILENAME_ARGUMENTS */ 215 216/* Invoke the command specified by an execute file. */ 217 218/*ARGSUSED*/ 219boolean 220fsysdep_execute (qsys, zuser, pazargs, zfullcmd, zinput, zoutput, 221 zchdir, fshell, iseq, pzerror, pftemp) 222 const struct uuconf_system *qsys; 223 const char *zuser; 224 const char **pazargs; 225 const char *zfullcmd ATTRIBUTE_UNUSED; 226 const char *zinput; 227 const char *zoutput; 228 const char *zchdir; 229 boolean fshell; 230 int iseq; 231 char **pzerror; 232 boolean *pftemp; 233{ 234 int aidescs[3]; 235 boolean ferr; 236 pid_t ipid; 237 int ierr; 238 char abxqtdir[sizeof XQTDIR + 4]; 239 const char *zxqtdir; 240 int istat; 241 char *zpath; 242#if ALLOW_SH_EXECUTION 243 const char *azshargs[4]; 244#endif 245 246 *pzerror = NULL; 247 *pftemp = FALSE; 248 249 aidescs[0] = SPAWN_NULL; 250 aidescs[1] = SPAWN_NULL; 251 aidescs[2] = SPAWN_NULL; 252 253 ferr = FALSE; 254 255 if (zinput != NULL) 256 { 257 aidescs[0] = open ((char *) zinput, O_RDONLY | O_NOCTTY, 0); 258 if (aidescs[0] < 0) 259 { 260 ulog (LOG_ERROR, "open (%s): %s", zinput, strerror (errno)); 261 ferr = TRUE; 262 } 263 else if (fcntl (aidescs[0], F_SETFD, 264 fcntl (aidescs[0], F_GETFD, 0) | FD_CLOEXEC) < 0) 265 { 266 ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); 267 ferr = TRUE; 268 } 269 } 270 271 if (! ferr && zoutput != NULL) 272 { 273 aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE); 274 if (aidescs[1] < 0) 275 { 276 if (errno == ENOENT && zoutput[0] != '/') 277 { 278 if (! fsysdep_make_dirs (zoutput, FALSE)) 279 { 280 *pftemp = TRUE; 281 ferr = TRUE; 282 } 283 else 284 aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE); 285 } 286 if (! ferr && aidescs[1] < 0) 287 { 288 ulog (LOG_ERROR, "creat during fsysdep_execute part A (%s) in %s: %s", zoutput, getwd(NULL), 289 strerror (errno)); 290 *pftemp = TRUE; 291 ferr = TRUE; 292 } 293 } 294 if (! ferr 295 && fcntl (aidescs[1], F_SETFD, 296 fcntl (aidescs[1], F_GETFD, 0) | FD_CLOEXEC) < 0) 297 { 298 ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); 299 ferr = TRUE; 300 } 301 } 302 303 if (! ferr) 304 { 305 *pzerror = zstemp_file (qsys); 306 aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE); 307 if (aidescs[2] < 0) 308 { 309 if (errno == ENOENT) 310 { 311 if (! fsysdep_make_dirs (*pzerror, FALSE)) 312 { 313 *pftemp = TRUE; 314 ferr = TRUE; 315 } 316 else 317 aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE); 318 } 319 if (! ferr && aidescs[2] < 0) 320 { 321 ulog (LOG_ERROR, "creat during fsysdep_execute part B (%s): %s", *pzerror, strerror (errno)); 322 *pftemp = TRUE; 323 ferr = TRUE; 324 } 325 } 326 if (! ferr 327 && fcntl (aidescs[2], F_SETFD, 328 fcntl (aidescs[2], F_GETFD, 0) | FD_CLOEXEC) < 0) 329 { 330 ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno)); 331 ferr = TRUE; 332 } 333 } 334 335 if (iseq == 0) 336 zxqtdir = XQTDIR; 337 else 338 { 339 sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); 340 zxqtdir = abxqtdir; 341 } 342 343 if (ferr) 344 { 345 if (aidescs[0] != SPAWN_NULL) 346 (void) close (aidescs[0]); 347 if (aidescs[1] != SPAWN_NULL) 348 (void) close (aidescs[1]); 349 if (aidescs[2] != SPAWN_NULL) 350 (void) close (aidescs[2]); 351 ubuffree (*pzerror); 352 return FALSE; 353 } 354 355#if ALLOW_SH_EXECUTION 356 if (fshell) 357 { 358 azshargs[0] = "/bin/sh"; 359 azshargs[1] = "-c"; 360 azshargs[2] = zfullcmd; 361 azshargs[3] = NULL; 362 pazargs = azshargs; 363 } 364#else 365 fshell = FALSE; 366#endif 367 368 if (qsys->uuconf_pzpath == NULL) 369 zpath = NULL; 370 else 371 { 372 size_t c; 373 char **pz; 374 375 c = 0; 376 for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) 377 c += strlen (*pz) + 1; 378 zpath = zbufalc (c); 379 *zpath = '\0'; 380 for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++) 381 { 382 strcat (zpath, *pz); 383 if (pz[1] != NULL) 384 strcat (zpath, ":"); 385 } 386 } 387 388 /* Pass zchdir as zxqtdir, fnosigs as TRUE, fshell as TRUE if we 389 aren't already using the shell. */ 390 ipid = ixsspawn (pazargs, aidescs, TRUE, FALSE, zchdir ? zchdir : zxqtdir, 391 TRUE, !fshell, zpath, qsys->uuconf_zname, zuser); 392 393 ierr = errno; 394 395 ubuffree (zpath); 396 397 if (aidescs[0] != SPAWN_NULL) 398 (void) close (aidescs[0]); 399 if (aidescs[1] != SPAWN_NULL) 400 (void) close (aidescs[1]); 401 if (aidescs[2] != SPAWN_NULL) 402 (void) close (aidescs[2]); 403 404 if (ipid < 0) 405 { 406 ulog (LOG_ERROR, "ixsspawn: %s", strerror (ierr)); 407 *pftemp = TRUE; 408 return FALSE; 409 } 410 411 istat = ixswait ((unsigned long) ipid, "Execution"); 412 413 if (istat == EX_TEMPFAIL) 414 *pftemp = TRUE; 415 416 return istat == 0; 417} 418 419/* Lock a uuxqt process. */ 420 421int 422ixsysdep_lock_uuxqt (zcmd, cmaxuuxqts) 423 const char *zcmd; 424 int cmaxuuxqts; 425{ 426 char ab[sizeof "LCK.XQT.9999"]; 427 int i; 428 429 if (cmaxuuxqts <= 0 || cmaxuuxqts >= 10000) 430 cmaxuuxqts = 9999; 431 for (i = 0; i < cmaxuuxqts; i++) 432 { 433 sprintf (ab, "LCK.XQT.%d", i); 434 if (fsdo_lock (ab, TRUE, (boolean *) NULL)) 435 break; 436 } 437 if (i >= cmaxuuxqts) 438 return -1; 439 440 if (zcmd != NULL) 441 { 442 char abcmd[sizeof "LXQ.123456789"]; 443 444 sprintf (abcmd, "LXQ.%.9s", zcmd); 445 abcmd[strcspn (abcmd, " \t/")] = '\0'; 446 if (! fsdo_lock (abcmd, TRUE, (boolean *) NULL)) 447 { 448 (void) fsdo_unlock (ab, TRUE); 449 return -1; 450 } 451 } 452 453 return i; 454} 455 456/* Unlock a uuxqt process. */ 457 458boolean 459fsysdep_unlock_uuxqt (iseq, zcmd, cmaxuuxqts) 460 int iseq; 461 const char *zcmd; 462 int cmaxuuxqts ATTRIBUTE_UNUSED; 463{ 464 char ab[sizeof "LCK.XQT.9999"]; 465 boolean fret; 466 467 fret = TRUE; 468 469 sprintf (ab, "LCK.XQT.%d", iseq); 470 if (! fsdo_unlock (ab, TRUE)) 471 fret = FALSE; 472 473 if (zcmd != NULL) 474 { 475 char abcmd[sizeof "LXQ.123456789"]; 476 477 sprintf (abcmd, "LXQ.%.9s", zcmd); 478 abcmd[strcspn (abcmd, " \t/")] = '\0'; 479 if (! fsdo_unlock (abcmd, TRUE)) 480 fret = FALSE; 481 } 482 483 return fret; 484} 485 486/* See whether a particular uuxqt command is locked (this depends on 487 the implementation of fsdo_lock). */ 488 489boolean 490fsysdep_uuxqt_locked (zcmd) 491 const char *zcmd; 492{ 493 char ab[sizeof "LXQ.123456789"]; 494 struct stat s; 495 496 sprintf (ab, "LXQ.%.9s", zcmd); 497 return stat (ab, &s) == 0; 498} 499 500/* Lock a particular execute file. */ 501 502boolean 503fsysdep_lock_uuxqt_file (zfile) 504 const char *zfile; 505{ 506 char *zcopy, *z; 507 boolean fret; 508 509 zcopy = zbufcpy (zfile); 510 511 z = strrchr (zcopy, '/'); 512 if (z == NULL) 513 *zcopy = 'L'; 514 else 515 *(z + 1) = 'L'; 516 517 fret = fsdo_lock (zcopy, TRUE, (boolean *) NULL); 518 ubuffree (zcopy); 519 return fret; 520} 521 522/* Unlock a particular execute file. */ 523 524boolean 525fsysdep_unlock_uuxqt_file (zfile) 526 const char *zfile; 527{ 528 char *zcopy, *z; 529 boolean fret; 530 531 zcopy = zbufcpy (zfile); 532 533 z = strrchr (zcopy, '/'); 534 if (z == NULL) 535 *zcopy = 'L'; 536 else 537 *(z + 1) = 'L'; 538 539 fret = fsdo_unlock (zcopy, TRUE); 540 ubuffree (zcopy); 541 return fret; 542} 543 544/* Lock the execute directory. Since we use a different directory 545 depending on which LCK.XQT.dddd file we got, there is actually no 546 need to create a lock file. We do make sure that the directory 547 exists, though, and that it is empty. */ 548 549boolean 550fsysdep_lock_uuxqt_dir (iseq) 551 int iseq; 552{ 553 const char *zxqtdir; 554 char abxqtdir[sizeof XQTDIR + 4]; 555 556 if (iseq == 0) 557 zxqtdir = XQTDIR; 558 else 559 { 560 sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); 561 zxqtdir = abxqtdir; 562 } 563 564 if (mkdir (zxqtdir, S_IRWXU) < 0 565 && errno != EEXIST 566 && errno != EISDIR) 567 { 568 ulog (LOG_ERROR, "mkdir (%s): %s", zxqtdir, strerror (errno)); 569 return FALSE; 570 } 571 572 return fclean_uuxqt_dir (zxqtdir); 573} 574 575/* Unlock the execute directory and clear it out. The lock is 576 actually the LCK.XQT.dddd file, so we don't unlock it, but we do 577 remove all the files. */ 578 579boolean 580fsysdep_unlock_uuxqt_dir (iseq) 581 int iseq; 582{ 583 const char *zxqtdir; 584 char abxqtdir[sizeof XQTDIR + 4]; 585 586 if (iseq == 0) 587 zxqtdir = XQTDIR; 588 else 589 { 590 sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); 591 zxqtdir = abxqtdir; 592 } 593 594 return fclean_uuxqt_dir (zxqtdir); 595} 596 597static boolean 598fclean_uuxqt_dir (zxqtdir) 599 const char *zxqtdir; 600{ 601 DIR *qdir; 602 603 qdir = opendir ((char *) zxqtdir); 604 if (qdir != NULL) 605 { 606 struct dirent *qentry; 607 608 while ((qentry = readdir (qdir)) != NULL) 609 { 610 char *z; 611 612 if (strcmp (qentry->d_name, ".") == 0 613 || strcmp (qentry->d_name, "..") == 0) 614 continue; 615 z = zsysdep_in_dir (zxqtdir, qentry->d_name); 616 if (remove (z) < 0) 617 { 618 int ierr; 619 620 ierr = errno; 621 if (! fsysdep_directory (z)) 622 ulog (LOG_ERROR, "remove (%s): %s", z, 623 strerror (ierr)); 624 else 625 (void) fsysdep_rmdir (z); 626 } 627 ubuffree (z); 628 } 629 630 closedir (qdir); 631 } 632 633 return TRUE; 634} 635 636/* Move files into the execution directory. */ 637 638boolean 639fsysdep_copy_uuxqt_files (cfiles, pzfrom, pzto, iseq, pzinput) 640 int cfiles; 641 const char *const *pzfrom; 642 const char *const *pzto; 643 int iseq; 644 char **pzinput; 645{ 646 char *zinput; 647 const char *zxqtdir; 648 char abxqtdir[sizeof XQTDIR + 4]; 649 int i; 650 651 if (pzinput == NULL) 652 zinput = NULL; 653 else 654 zinput = *pzinput; 655 656 if (iseq == 0) 657 zxqtdir = XQTDIR; 658 else 659 { 660 sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); 661 zxqtdir = abxqtdir; 662 } 663 664 for (i = 0; i < cfiles; i++) 665 { 666 const char *zfrom, *zto; 667 char *zfree; 668 669 if (pzto[i] == NULL) 670 continue; 671 672 zfree = zsysdep_in_dir (zxqtdir, pzto[i]); 673 674 zfrom = pzfrom[i]; 675 zto = zfree; 676 677 if (zinput != NULL && strcmp (zinput, zfrom) == 0) 678 { 679 *pzinput = zbufcpy (zto); 680 zinput = NULL; 681 } 682 683 if (link (zfrom, zto) < 0) 684 { 685 if (errno != EXDEV && errno != EEXIST && errno != EMLINK) 686 { 687 ulog (LOG_ERROR, "link (%s, %s): %s", zfrom, zto, 688 strerror (errno)); 689 ubuffree (zfree); 690 return FALSE; 691 } 692 693 if (! fcopy_file (zfrom, zto, FALSE, FALSE, FALSE)) 694 { 695 ubuffree (zfree); 696 return FALSE; 697 } 698 } 699 700 (void) chmod (zto, IPUBLIC_FILE_MODE); 701#ifdef WORLD_WRITABLE_FILE_IN 702 char rfile[PATH_MAX]; 703 realpath(zto, rfile); 704 ulog(LOG_ERROR, "open of %s WORLD_WRITABLE_FILE_IN is %s", rfile, WORLD_WRITABLE_FILE_IN); 705 if (rfile == strstr(rfile, WORLD_WRITABLE_FILE_IN)) { 706 chmod(zto, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 707 ulog(LOG_ERROR, "xqtsub chmod'ing %s", zto); 708 } 709#endif 710 711 ubuffree (zfree); 712 } 713 714 return TRUE; 715} 716