1 /* $OpenBSD: common.c,v 1.41 2022/12/26 19:16:02 jmc Exp $ */ 2 3 /* 4 * Copyright (c) 1983 Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/stat.h> 33 #include <sys/time.h> 34 #include <sys/wait.h> 35 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <grp.h> 39 #include <limits.h> 40 #include <paths.h> 41 #include <stdarg.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #include "defs.h" 48 49 /* 50 * Things common to both the client and server. 51 */ 52 53 /* 54 * Variables common to both client and server 55 */ 56 char host[HOST_NAME_MAX+1]; /* Name of this host */ 57 uid_t userid = (uid_t)-1; /* User's UID */ 58 gid_t groupid = (gid_t)-1; /* User's GID */ 59 gid_t gidset[NGROUPS_MAX]; /* User's GID list */ 60 int gidsetlen = 0; /* Number of GIDS in list */ 61 char *homedir = NULL; /* User's $HOME */ 62 char *locuser = NULL; /* Local User's name */ 63 int isserver = FALSE; /* We're the server */ 64 int amchild = 0; /* This PID is a child */ 65 int do_fork = 1; /* Fork child process */ 66 char *currenthost = NULL; /* Current client hostname */ 67 char *progname = NULL; /* Name of this program */ 68 int rem_r = -1; /* Client file descriptor */ 69 int rem_w = -1; /* Client file descriptor */ 70 volatile sig_atomic_t contimedout = FALSE; /* Connection timed out */ 71 int rtimeout = RTIMEOUT; /* Response time out */ 72 jmp_buf finish_jmpbuf; /* Finish() jmp buffer */ 73 int setjmp_ok = FALSE; /* setjmp()/longjmp() status */ 74 char **realargv; /* Real main() argv */ 75 int realargc; /* Real main() argc */ 76 opt_t options = 0; /* Global install options */ 77 char defowner[64] = "bin"; /* Default owner */ 78 char defgroup[64] = "bin"; /* Default group */ 79 80 static int sendcmdmsg(int, char *, size_t); 81 static ssize_t remread(int, u_char *, size_t); 82 static int remmore(void); 83 84 /* 85 * Front end to write() that handles partial write() requests. 86 */ 87 ssize_t 88 xwrite(int fd, void *buf, size_t len) 89 { 90 size_t nleft = len; 91 ssize_t nwritten; 92 char *ptr = buf; 93 94 while (nleft > 0) { 95 if ((nwritten = write(fd, ptr, nleft)) <= 0) { 96 return nwritten; 97 } 98 nleft -= nwritten; 99 ptr += nwritten; 100 } 101 102 return len; 103 } 104 105 /* 106 * Do run-time initialization 107 */ 108 int 109 init(int argc, char **argv, char **envp) 110 { 111 struct passwd *pw; 112 int i; 113 114 /* 115 * Save a copy of our argc and argv before setargs() overwrites them 116 */ 117 realargc = argc; 118 realargv = xmalloc(sizeof(char *) * (argc+1)); 119 for (i = 0; i < argc; i++) 120 realargv[i] = xstrdup(argv[i]); 121 122 pw = getpwuid(userid = getuid()); 123 if (pw == NULL) { 124 error("Your user id (%u) is not known to this system.", 125 getuid()); 126 return(-1); 127 } 128 129 debugmsg(DM_MISC, "UserID = %u pwname = '%s' home = '%s'\n", 130 userid, pw->pw_name, pw->pw_dir); 131 homedir = xstrdup(pw->pw_dir); 132 locuser = xstrdup(pw->pw_name); 133 groupid = pw->pw_gid; 134 gidsetlen = getgroups(NGROUPS_MAX, gidset); 135 gethostname(host, sizeof(host)); 136 #if 0 137 if ((cp = strchr(host, '.')) != NULL) 138 *cp = CNULL; 139 #endif 140 141 /* 142 * If we're not root, disable paranoid ownership checks 143 * since normal users cannot chown() files. 144 */ 145 if (!isserver && userid != 0) { 146 FLAG_ON(options, DO_NOCHKOWNER); 147 FLAG_ON(options, DO_NOCHKGROUP); 148 } 149 150 return(0); 151 } 152 153 /* 154 * Finish things up before ending. 155 */ 156 void 157 finish(void) 158 { 159 debugmsg(DM_CALL, 160 "finish() called: do_fork = %d amchild = %d isserver = %d", 161 do_fork, amchild, isserver); 162 cleanup(0); 163 164 /* 165 * There's no valid finish_jmpbuf for the rdist master parent. 166 */ 167 if (!do_fork || amchild || isserver) { 168 169 if (!setjmp_ok) { 170 #ifdef DEBUG_SETJMP 171 error("attempting longjmp() without target"); 172 abort(); 173 #else 174 exit(1); 175 #endif 176 } 177 178 longjmp(finish_jmpbuf, 1); 179 /*NOTREACHED*/ 180 error("Unexpected failure of longjmp() in finish()"); 181 exit(2); 182 } else 183 exit(1); 184 } 185 186 /* 187 * Handle lost connections 188 */ 189 void 190 lostconn(void) 191 { 192 /* Prevent looping */ 193 (void) signal(SIGPIPE, SIG_IGN); 194 195 rem_r = rem_w = -1; /* Ensure we don't try to send to server */ 196 checkhostname(); 197 error("Lost connection to %s", 198 (currenthost) ? currenthost : "(unknown)"); 199 200 finish(); 201 } 202 203 /* 204 * General signal handler 205 */ 206 void 207 sighandler(int sig) 208 { 209 int save_errno = errno; 210 211 /* XXX signal race */ 212 debugmsg(DM_CALL, "sighandler() received signal %d\n", sig); 213 214 switch (sig) { 215 case SIGALRM: 216 contimedout = TRUE; 217 /* XXX signal race */ 218 checkhostname(); 219 error("Response time out"); 220 finish(); 221 break; 222 223 case SIGPIPE: 224 /* XXX signal race */ 225 lostconn(); 226 break; 227 228 case SIGFPE: 229 debug = !debug; 230 break; 231 232 case SIGHUP: 233 case SIGINT: 234 case SIGQUIT: 235 case SIGTERM: 236 /* XXX signal race */ 237 finish(); 238 break; 239 240 default: 241 /* XXX signal race */ 242 fatalerr("No signal handler defined for signal %d.", sig); 243 } 244 errno = save_errno; 245 } 246 247 /* 248 * Function to actually send the command char and message to the 249 * remote host. 250 */ 251 static int 252 sendcmdmsg(int cmd, char *msg, size_t msgsize) 253 { 254 int len; 255 256 if (rem_w < 0) 257 return(-1); 258 259 /* 260 * All commands except C_NONE should have a newline 261 */ 262 if (cmd != C_NONE && !strchr(msg + 1, '\n')) 263 (void) strlcat(msg + 1, "\n", msgsize - 1); 264 265 if (cmd == C_NONE) 266 len = strlen(msg); 267 else { 268 len = strlen(msg + 1) + 1; 269 msg[0] = cmd; 270 } 271 272 debugmsg(DM_PROTO, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"", 273 cmd, cmd, 274 (cmd == C_NONE) ? len-1 : len-2, 275 (cmd == C_NONE) ? msg : msg + 1); 276 277 return(!(xwrite(rem_w, msg, len) == len)); 278 } 279 280 /* 281 * Send a command message to the remote host. 282 * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...) 283 * The fmt may be NULL, in which case there are no args. 284 */ 285 int 286 sendcmd(char cmd, const char *fmt, ...) 287 { 288 static char buf[BUFSIZ]; 289 va_list args; 290 291 va_start(args, fmt); 292 if (fmt) 293 (void) vsnprintf(buf + (cmd != C_NONE), 294 sizeof(buf) - (cmd != C_NONE), fmt, args); 295 else 296 buf[1] = CNULL; 297 va_end(args); 298 299 return(sendcmdmsg(cmd, buf, sizeof(buf))); 300 } 301 302 /* 303 * Internal variables and routines for reading lines from the remote. 304 */ 305 static u_char rembuf[BUFSIZ]; 306 static u_char *remptr; 307 static ssize_t remleft; 308 309 #define remc() (--remleft < 0 ? remmore() : *remptr++) 310 311 /* 312 * Back end to remote read() 313 */ 314 static ssize_t 315 remread(int fd, u_char *buf, size_t bufsiz) 316 { 317 return(read(fd, (char *)buf, bufsiz)); 318 } 319 320 static int 321 remmore(void) 322 { 323 (void) signal(SIGALRM, sighandler); 324 (void) alarm(rtimeout); 325 326 remleft = remread(rem_r, rembuf, sizeof(rembuf)); 327 328 (void) alarm(0); 329 330 if (remleft < 0) 331 return (-2); /* error */ 332 if (remleft == 0) 333 return (-1); /* EOF */ 334 remptr = rembuf; 335 remleft--; 336 return (*remptr++); 337 } 338 339 /* 340 * Read an input line from the remote. Return the number of bytes 341 * stored (equivalent to strlen(p)). If `cleanup' is set, EOF at 342 * the beginning of a line is returned as EOF (-1); other EOFs, or 343 * errors, call cleanup() or lostconn(). In other words, unless 344 * the third argument is nonzero, this routine never returns failure. 345 */ 346 int 347 remline(u_char *buffer, int space, int doclean) 348 { 349 int c, left = space; 350 u_char *p = buffer; 351 352 if (rem_r < 0) { 353 error("Cannot read remote input: Remote descriptor not open."); 354 return(-1); 355 } 356 357 while (left > 0) { 358 if ((c = remc()) < -1) { /* error */ 359 if (doclean) { 360 finish(); 361 /*NOTREACHED*/ 362 } 363 lostconn(); 364 /*NOTREACHED*/ 365 } 366 if (c == -1) { /* got EOF */ 367 if (doclean) { 368 if (left == space) 369 return (-1);/* signal proper EOF */ 370 finish(); /* improper EOF */ 371 /*NOTREACHED*/ 372 } 373 lostconn(); 374 /*NOTREACHED*/ 375 } 376 if (c == '\n') { 377 *p = CNULL; 378 379 if (debug) { 380 static char mbuf[BUFSIZ]; 381 382 (void) snprintf(mbuf, sizeof(mbuf), 383 "<<< Cmd = %c (\\%3.3o) Msg = \"%s\"", 384 buffer[0], buffer[0], 385 buffer + 1); 386 387 debugmsg(DM_PROTO, "%s", mbuf); 388 } 389 390 return (space - left); 391 } 392 *p++ = c; 393 left--; 394 } 395 396 /* this will probably blow the entire session */ 397 error("remote input line too long"); 398 p[-1] = CNULL; /* truncate */ 399 return (space); 400 } 401 402 /* 403 * Non-line-oriented remote read. 404 */ 405 ssize_t 406 readrem(char *p, ssize_t space) 407 { 408 if (remleft <= 0) { 409 /* 410 * Set remote time out alarm. 411 */ 412 (void) signal(SIGALRM, sighandler); 413 (void) alarm(rtimeout); 414 415 remleft = remread(rem_r, rembuf, sizeof(rembuf)); 416 417 (void) alarm(0); 418 remptr = rembuf; 419 } 420 421 if (remleft <= 0) 422 return (remleft); 423 if (remleft < space) 424 space = remleft; 425 426 memcpy(p, remptr, space); 427 428 remptr += space; 429 remleft -= space; 430 431 return (space); 432 } 433 434 /* 435 * Get the user name for the uid. 436 */ 437 char * 438 getusername(uid_t uid, char *file, opt_t opts) 439 { 440 static char buf[100]; 441 static uid_t lastuid = (uid_t)-1; 442 const char *name; 443 444 /* 445 * The value of opts may have changed so we always 446 * do the opts check. 447 */ 448 if (IS_ON(opts, DO_NUMCHKOWNER)) { 449 (void) snprintf(buf, sizeof(buf), ":%u", uid); 450 return(buf); 451 } 452 453 /* 454 * Try to avoid passwd lookup. 455 */ 456 if (lastuid == uid && buf[0] != '\0' && buf[0] != ':') 457 return(buf); 458 459 lastuid = uid; 460 461 if ((name = user_from_uid(uid, 1)) == NULL) { 462 if (IS_ON(opts, DO_DEFOWNER) && !isserver) 463 (void) strlcpy(buf, defowner, sizeof(buf)); 464 else { 465 message(MT_WARNING, 466 "%s: No password entry for uid %u", file, uid); 467 (void) snprintf(buf, sizeof(buf), ":%u", uid); 468 } 469 } else { 470 (void) strlcpy(buf, name, sizeof(buf)); 471 } 472 473 return(buf); 474 } 475 476 /* 477 * Get the group name for the gid. 478 */ 479 char * 480 getgroupname(gid_t gid, char *file, opt_t opts) 481 { 482 static char buf[100]; 483 static gid_t lastgid = (gid_t)-1; 484 const char *name; 485 486 /* 487 * The value of opts may have changed so we always 488 * do the opts check. 489 */ 490 if (IS_ON(opts, DO_NUMCHKGROUP)) { 491 (void) snprintf(buf, sizeof(buf), ":%u", gid); 492 return(buf); 493 } 494 495 /* 496 * Try to avoid group lookup. 497 */ 498 if (lastgid == gid && buf[0] != '\0' && buf[0] != ':') 499 return(buf); 500 501 lastgid = gid; 502 503 if ((name = group_from_gid(gid, 1)) == NULL) { 504 if (IS_ON(opts, DO_DEFGROUP) && !isserver) 505 (void) strlcpy(buf, defgroup, sizeof(buf)); 506 else { 507 message(MT_WARNING, "%s: No name for group %u", 508 file, gid); 509 (void) snprintf(buf, sizeof(buf), ":%u", gid); 510 } 511 } else 512 (void) strlcpy(buf, name, sizeof(buf)); 513 514 return(buf); 515 } 516 517 /* 518 * Read a response from the remote host. 519 */ 520 int 521 response(void) 522 { 523 static u_char resp[BUFSIZ]; 524 u_char *s; 525 int n; 526 527 debugmsg(DM_CALL, "response() start\n"); 528 529 n = remline(s = resp, sizeof(resp), 0); 530 531 n--; 532 switch (*s++) { 533 case C_ACK: 534 debugmsg(DM_PROTO, "received ACK\n"); 535 return(0); 536 case C_LOGMSG: 537 if (n > 0) { 538 message(MT_CHANGE, "%s", s); 539 return(1); 540 } 541 debugmsg(DM_PROTO, "received EMPTY logmsg\n"); 542 return(0); 543 case C_NOTEMSG: 544 if (s) 545 message(MT_NOTICE, "%s", s); 546 return(response()); 547 548 default: 549 s--; 550 n++; 551 /* fall into... */ 552 553 case C_ERRMSG: /* Normal error message */ 554 if (s) 555 message(MT_NERROR, "%s", s); 556 return(-1); 557 558 case C_FERRMSG: /* Fatal error message */ 559 if (s) 560 message(MT_FERROR, "%s", s); 561 finish(); 562 return(-1); 563 } 564 /*NOTREACHED*/ 565 } 566 567 /* 568 * This should be in expand.c but the other routines call other modules 569 * that we don't want to load in. 570 * 571 * Expand file names beginning with `~' into the 572 * user's home directory path name. Return a pointer in buf to the 573 * part corresponding to `file'. 574 */ 575 char * 576 exptilde(char *ebuf, char *file, size_t ebufsize) 577 { 578 struct passwd *pw; 579 char *pw_dir, *rest; 580 static char lastuser[_PW_NAME_LEN + 1]; 581 static char lastdir[PATH_MAX]; 582 size_t len; 583 584 if (*file != '~') { 585 notilde: 586 (void) strlcpy(ebuf, file, ebufsize); 587 return(ebuf); 588 } 589 pw_dir = homedir; 590 if (*++file == CNULL) { 591 rest = NULL; 592 } else if (*file == '/') { 593 rest = file; 594 } else { 595 rest = file; 596 while (*rest && *rest != '/') 597 rest++; 598 if (*rest == '/') 599 *rest = CNULL; 600 else 601 rest = NULL; 602 if (strcmp(locuser, file) != 0) { 603 if (strcmp(lastuser, file) != 0) { 604 if ((pw = getpwnam(file)) == NULL) { 605 error("%s: unknown user name", file); 606 if (rest != NULL) 607 *rest = '/'; 608 return(NULL); 609 } 610 strlcpy(lastuser, pw->pw_name, sizeof(lastuser)); 611 strlcpy(lastdir, pw->pw_dir, sizeof(lastdir)); 612 } 613 pw_dir = lastdir; 614 } 615 if (rest != NULL) 616 *rest = '/'; 617 } 618 if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize) 619 goto notilde; 620 pw_dir = ebuf + len; 621 if (rest != NULL) { 622 pw_dir++; 623 if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize) 624 goto notilde; 625 } 626 return(pw_dir); 627 } 628 629 630 631 /* 632 * Set access and modify times of a given file 633 */ 634 int 635 setfiletime(char *file, time_t atime, time_t mtime) 636 { 637 struct timeval tv[2]; 638 639 if (atime != 0 && mtime != 0) { 640 tv[0].tv_sec = atime; 641 tv[1].tv_sec = mtime; 642 tv[0].tv_usec = tv[1].tv_usec = 0; 643 return (utimes(file, tv)); 644 } else /* Set to current time */ 645 return (utimes(file, NULL)); 646 } 647 648 /* 649 * Get version info 650 */ 651 char * 652 getversion(void) 653 { 654 static char buff[BUFSIZ]; 655 656 (void) snprintf(buff, sizeof(buff), 657 "Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d", 658 DISTVERSION, PATCHLEVEL, DISTSTATUS, 659 VERSION, DISTVERSION, PATCHLEVEL); 660 661 return(buff); 662 } 663 664 /* 665 * Execute a shell command to handle special cases. 666 * This is now common to both server and client 667 */ 668 void 669 runcommand(char *cmd) 670 { 671 ssize_t nread; 672 pid_t pid, wpid; 673 char *cp, *s; 674 char sbuf[BUFSIZ], buf[BUFSIZ]; 675 int fd[2], status; 676 677 if (pipe(fd) == -1) { 678 error("pipe of %s failed: %s", cmd, SYSERR); 679 return; 680 } 681 682 if ((pid = fork()) == 0) { 683 /* 684 * Return everything the shell commands print. 685 */ 686 (void) close(0); 687 (void) close(1); 688 (void) close(2); 689 (void) open(_PATH_DEVNULL, O_RDONLY); 690 (void) dup(fd[PIPE_WRITE]); 691 (void) dup(fd[PIPE_WRITE]); 692 (void) close(fd[PIPE_READ]); 693 (void) close(fd[PIPE_WRITE]); 694 (void) execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL); 695 _exit(127); 696 } 697 (void) close(fd[PIPE_WRITE]); 698 s = sbuf; 699 *s++ = C_LOGMSG; 700 while ((nread = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) { 701 cp = buf; 702 do { 703 *s++ = *cp++; 704 if (cp[-1] != '\n') { 705 if (s < (char *) &sbuf[sizeof(sbuf)-1]) 706 continue; 707 *s++ = '\n'; 708 } 709 /* 710 * Throw away blank lines. 711 */ 712 if (s == &sbuf[2]) { 713 s--; 714 continue; 715 } 716 if (isserver) 717 (void) xwrite(rem_w, sbuf, s - sbuf); 718 else { 719 *s = CNULL; 720 message(MT_INFO, "%s", sbuf+1); 721 } 722 s = &sbuf[1]; 723 } while (--nread); 724 } 725 if (s > (char *) &sbuf[1]) { 726 *s++ = '\n'; 727 if (isserver) 728 (void) xwrite(rem_w, sbuf, s - sbuf); 729 else { 730 *s = CNULL; 731 message(MT_INFO, "%s", sbuf+1); 732 } 733 } 734 while ((wpid = wait(&status)) != pid && wpid != -1) 735 ; 736 if (wpid == -1) 737 status = -1; 738 (void) close(fd[PIPE_READ]); 739 if (status) 740 error("shell returned %d", status); 741 else if (isserver) 742 ack(); 743 } 744 745 /* 746 * Malloc with error checking 747 */ 748 void * 749 xmalloc(size_t amt) 750 { 751 void *ptr; 752 753 if ((ptr = malloc(amt)) == NULL) 754 fatalerr("Cannot malloc %zu bytes of memory.", amt); 755 756 return (ptr); 757 } 758 759 /* 760 * realloc with error checking 761 */ 762 void * 763 xrealloc(void *baseptr, size_t amt) 764 { 765 void *new; 766 767 if ((new = realloc(baseptr, amt)) == NULL) 768 fatalerr("Cannot realloc %zu bytes of memory.", amt); 769 770 return (new); 771 } 772 773 /* 774 * calloc with error checking 775 */ 776 void * 777 xcalloc(size_t num, size_t esize) 778 { 779 void *ptr; 780 781 if ((ptr = calloc(num, esize)) == NULL) 782 fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.", 783 num, esize, num * esize); 784 785 return (ptr); 786 } 787 788 /* 789 * Strdup with error checking 790 */ 791 char * 792 xstrdup(const char *str) 793 { 794 size_t len = strlen(str) + 1; 795 char *nstr = xmalloc(len); 796 797 return (memcpy(nstr, str, len)); 798 } 799 800 /* 801 * Private version of basename() 802 */ 803 char * 804 xbasename(char *path) 805 { 806 char *cp; 807 808 if ((cp = strrchr(path, '/')) != NULL) 809 return(cp+1); 810 else 811 return(path); 812 } 813 814 /* 815 * Take a colon (':') separated path to a file and 816 * search until a component of that path is found and 817 * return the found file name. 818 */ 819 char * 820 searchpath(char *path) 821 { 822 char *file; 823 char *space; 824 int found; 825 struct stat statbuf; 826 827 for (found = 0; !found && (file = strsep(&path, ":")) != NULL; ) { 828 if ((space = strchr(file, ' ')) != NULL) 829 *space = CNULL; 830 found = stat(file, &statbuf) == 0; 831 if (space) 832 *space = ' '; /* Put back what we zapped */ 833 } 834 return (file); 835 } 836