1/* $NetBSD: supfilesrv.c,v 1.46 2011/03/17 19:43:34 christos Exp $ */ 2 3/* 4 * Copyright (c) 1992 Carnegie Mellon University 5 * All Rights Reserved. 6 * 7 * Permission to use, copy, modify and distribute this software and its 8 * documentation is hereby granted, provided that both the copyright 9 * notice and this permission notice appear in all copies of the 10 * software, derivative works or modified versions, and any portions 11 * thereof, and that both notices appear in supporting documentation. 12 * 13 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 14 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 15 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 16 * 17 * Carnegie Mellon requests users of this software to return to 18 * 19 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 20 * School of Computer Science 21 * Carnegie Mellon University 22 * Pittsburgh PA 15213-3890 23 * 24 * any improvements or extensions that they make and grant Carnegie Mellon 25 * the rights to redistribute these changes. 26 * 27 */ 28/* 29 * supfilesrv -- SUP File Server 30 * 31 * Usage: supfilesrv [-d] [-l] [-P] [-N] [-R] [-S] 32 * -d "debug" -- don't fork daemon 33 * -l "log" -- print successull connects (when compiled with libwrap) 34 * -P "debug ports" -- use debugging network ports 35 * -N "debug network" -- print debugging messages for network i/o 36 * -R "RCS mode" -- if file is an rcs file, use co to get contents 37 * -S "Operate silently" -- Only print error messages 38 * 39 ********************************************************************** 40 * HISTORY 41 * 2-Aug-99 Manuel Bouyer at LIP6 42 * Added libwrap support 43 * 44 * 13-Sep-92 Mary Thompson (mrt) at Carnegie-Mellon University 45 * Changed name of sup program in xpatch from /usr/cs/bin/sup to 46 * /usr/bin/sup for exported version of sup. 47 * 48 * 7-July-93 Nate Williams at Montana State University 49 * Modified SUP to use gzip based compression when sending files 50 * across the network to save BandWidth 51 * 52 * Revision 1.20 92/09/09 22:05:00 mrt 53 * Added Brad's change to make send_file take a va_list. 54 * Added support in login to accept an non-encrypted login 55 * message if no user or password is being sent. This supports 56 * a non-crypting version of sup. Also fixed to skip leading 57 * white space from crypts in host files. 58 * [92/09/01 mrt] 59 * 60 * Revision 1.19 92/08/11 12:07:59 mrt 61 * Made maxchildren a patchable variable, which can be set by the 62 * command line switch -C or else defaults to the MAXCHILDREN 63 * defined in sup.h. Added most of Brad's STUMP changes. 64 * Increased PGMVERSION to 12 to reflect substantial changes. 65 * [92/07/28 mrt] 66 * 67 * Revision 1.18 90/12/25 15:15:39 ern 68 * Yet another rewrite of the logging code. Make up the text we will write 69 * and then get in, write it and get out. 70 * Also set error on write-to-full-disk if the logging is for recording 71 * server is busy. 72 * [90/12/25 15:15:15 ern] 73 * 74 * Revision 1.17 90/05/07 09:31:13 dlc 75 * Sigh, some more fixes to the new "crypt" file handling code. First, 76 * just because the "crypt" file is in a local file system does not mean 77 * it can be trusted. We have to check for hard links to root owned 78 * files whose contents could be interpretted as a crypt key. For 79 * checking this fact, the new routine stat_info_ok() was added. This 80 * routine also makes other sanity checks, such as owner only permission, 81 * the file is a regular file, etc. Also, even if the uid/gid of th 82 * "crypt" file is not going to be used, still use its contents in order 83 * to cause fewer surprises to people supping out of a shared file system 84 * such as AFS. 85 * [90/05/07 dlc] 86 * 87 * Revision 1.16 90/04/29 04:21:08 dlc 88 * Fixed logic bug in docrypt() which would not get the stat information 89 * from the crypt file if the crypt key had already been set from a 90 * "host" file. 91 * [90/04/29 dlc] 92 * 93 * Revision 1.15 90/04/18 19:51:27 dlc 94 * Added the new routines local_file(), link_nofollow() for use in 95 * dectecting whether a file is located in a local file system. These 96 * routines probably should have been in another module, but only 97 * supfilesrv needs to do the check and none of its other modules seemed 98 * appropriate. Note, the implementation should be changed once we have 99 * direct kernel support, for example the fstatvfs(2) system call, for 100 * detecting the type of file system a file resides. Also, I changed 101 * the routines which read the crosspatch crypt file or collection crypt 102 * file to save the uid and gid from the stat information obtained via 103 * the local_file() call (when the file is local) at the same time the 104 * crypt key is read. This change disallows non-local files for the 105 * crypt key to plug a security hole involving the usage of the uid/gid 106 * of the crypt file to define who the file server should run as. If 107 * the saved uid/gid are both valid, then the server will set its uid/gid 108 * to these values. 109 * [90/04/18 dlc] 110 * 111 * Revision 1.14 89/08/23 14:56:15 gm0w 112 * Changed msgf routines to msg routines. 113 * [89/08/23 gm0w] 114 * 115 * Revision 1.13 89/08/03 19:57:33 mja 116 * Remove setaid() call. 117 * 118 * Revision 1.12 89/08/03 19:49:24 mja 119 * Updated to use v*printf() in place of _doprnt(). 120 * [89/04/19 mja] 121 * 122 * 11-Sep-88 Glenn Marcy (gm0w) at Carnegie-Mellon University 123 * Added code to record release name in logfile. 124 * 125 * 18-Mar-88 Glenn Marcy (gm0w) at Carnegie-Mellon University 126 * Added host=<hostfile> support to releases file. [V7.12] 127 * 128 * 27-Dec-87 Glenn Marcy (gm0w) at Carnegie-Mellon University 129 * Added crosspatch support. Created docrypt() routine for crypt 130 * test message. 131 * 132 * 09-Sep-87 Glenn Marcy (gm0w) at Carnegie-Mellon University 133 * Removed common information logging code, the quiet switch, and 134 * moved samehost() check to after device/inode check. 135 * 136 * 28-Jun-87 Glenn Marcy (gm0w) at Carnegie-Mellon University 137 * Added code for "release" support. [V5.11] 138 * 139 * 26-May-87 Doug Philips (dwp) at Carnegie-Mellon University 140 * Added code to record final status of client in logfile. [V5.10] 141 * 142 * 22-May-87 Chriss Stephens (chriss) at Carnegie Mellon University 143 * Mergered divergent CS and ECE versions. [V5.9a] 144 * 145 * 20-May-87 Glenn Marcy (gm0w) at Carnegie-Mellon University 146 * Removed support for version 3 of SUP protocol. Added changes 147 * to make lint happy. Added calls to new logging routines. [V5.9] 148 * 149 * 31-Mar-87 Dan Nydick (dan) at Carnegie-Mellon University 150 * Fixed so no password check is done when crypts are used. 151 * 152 * 25-Nov-86 Rudy Nedved (ern) at Carnegie-Mellon University 153 * Set F_APPEND fcntl in logging to increase the chance 154 * that the log entry from this incarnation of the file 155 * server will not be lost by another incarnation. [V5.8] 156 * 157 * 20-Oct-86 Dan Nydick (dan) at Carnegie-Mellon University 158 * Changed not to call okmumbles when not compiled with CMUCS. 159 * 160 * 04-Aug-86 Glenn Marcy (gm0w) at Carnegie-Mellon University 161 * Added code to increment scmdebug as more -N flags are 162 * added. [V5.7] 163 * 164 * 25-May-86 Jonathan J. Chew (jjc) at Carnegie-Mellon University 165 * Renamed local variable in main program from "sigmask" to 166 * "signalmask" to avoid name conflict with 4.3BSD identifier. 167 * Conditionally compile in calls to CMU routines, "setaid" and 168 * "logaccess". [V5.6] 169 * 170 * 21-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University 171 * Changed supfilesrv to use the crypt file owner and group for 172 * access purposes, rather than the directory containing the crypt 173 * file. [V5.5] 174 * 175 * 07-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University 176 * Added code to keep logfiles in repository collection directory. 177 * Added code for locking collections. [V5.4] 178 * 179 * 05-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University 180 * Added code to support new FSETUPBUSY return. Now accepts all 181 * connections and tells any clients after the 8th that the 182 * fileserver is busy. New clients will retry again later. [V5.3] 183 * 184 * 29-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University 185 * Major rewrite for protocol version 4. [V4.2] 186 * 187 * 12-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University 188 * Fixed close of crypt file to use file pointer as argument 189 * instead of string pointer. 190 * 191 * 24-Nov-85 Glenn Marcy (gm0w) at Carnegie-Mellon University 192 * Allow "!hostname" lines and comments in collection "host" file. 193 * 194 * 13-Nov-85 Glenn Marcy (gm0w) at Carnegie-Mellon University 195 * Don't use access() on symbolic links since they may not point to 196 * an existing file. 197 * 198 * 22-Oct-85 Glenn Marcy (gm0w) at Carnegie-Mellon University 199 * Added code to restrict file server availability to when it has 200 * less than or equal to eight children. 201 * 202 * 22-Sep-85 Glenn Marcy (gm0w) at Carnegie-Mellon University 203 * Merged 4.1 and 4.2 versions together. 204 * 205 * 04-Jun-85 Steven Shafer (sas) at Carnegie-Mellon University 206 * Created for 4.2 BSD. 207 * 208 ********************************************************************** 209 */ 210 211#ifdef AFS 212#include <afs/param.h> 213#undef MAXNAMLEN 214#endif 215#include <sys/param.h> 216#include <signal.h> 217#include <errno.h> 218#include <setjmp.h> 219#include <pwd.h> 220#include <grp.h> 221#include <fcntl.h> 222#include <stdarg.h> 223#include <sys/time.h> 224#include <sys/resource.h> 225#include <sys/wait.h> 226#include <sys/stat.h> 227#include <sys/file.h> 228#include <sys/mount.h> 229#include <sys/socket.h> 230#ifndef HAS_POSIX_DIR 231#include <sys/dir.h> 232#else 233#include <dirent.h> 234#endif 235#if MACH 236#include <sys/ioctl.h> 237#endif 238#if CMUCS 239#include <acc.h> 240#include <sys/ttyloc.h> 241#include <access.h> 242#include <sys/viceioctl.h> 243#else /* CMUCS */ 244#define ACCESS_CODE_OK 0 245#define ACCESS_CODE_BADPASSWORD (-2) 246#endif /* CMUCS */ 247 248#ifdef __SVR4 249#include <sys/mkdev.h> 250#include <sys/statvfs.h> 251#endif 252#ifdef LIBWRAP 253#include <tcpd.h> 254#endif 255 256#include "supcdefs.h" 257#include "supextern.h" 258#define MSGFILE 259#include "supmsg.h" 260#include "libc.h" 261#include "c.h" 262 263extern char *crypt(const char *, const char *); 264 265int maxchildren; 266 267/* 268 * These are used to save the stat information from the crosspatch crypt 269 * file or collection crypt file at the time it is opened for the crypt 270 * key and it is verified to be a local file. 271 */ 272int runas_uid = -1; 273int runas_gid = -1; 274 275#define PGMVERSION 13 276 277/************************* 278 *** M A C R O S *** 279 *************************/ 280 281#define HASHBITS 8 282#define HASHSIZE (1<<HASHBITS) 283#define HASHMASK (HASHSIZE-1) 284#define HASHFUNC(x,y) ((x)&HASHMASK) 285 286/******************************************* 287 *** D A T A S T R U C T U R E S *** 288 *******************************************/ 289 290struct hashstruct { /* hash table for number lists */ 291 int Hnum1; /* numeric keys */ 292 int Hnum2; 293 char *Hname; /* string value */ 294 TREE *Htree; /* TREE value */ 295 struct hashstruct *Hnext; 296}; 297typedef struct hashstruct HASH; 298 299/********************************************* 300 *** G L O B A L V A R I A B L E S *** 301 *********************************************/ 302 303char program[] = "supfilesrv"; /* program name for SCM messages */ 304int progpid = -1; /* and process id */ 305 306jmp_buf sjbuf; /* jump location for network errors */ 307TREELIST *listTL; /* list of trees to upgrade */ 308 309int silent; /* -S flag */ 310#ifdef LIBWRAP 311int sup_clog; /* -l flag */ 312#endif 313int live; /* -d flag */ 314int dbgportsq; /* -P flag */ 315extern int scmdebug; /* -N flag */ 316extern int netfile; 317#ifdef RCS 318int candorcs; /* -R flag */ 319int dorcs = FALSE; 320#endif 321 322int nchildren; /* number of children that exist */ 323char *prefix; /* collection pathname prefix */ 324char *release; /* collection release name */ 325char *cryptkey; /* encryption key if non-null */ 326#ifdef CVS 327char *cvs_root; /* RCS root */ 328#endif 329char *rcs_branch; /* RCS branch name */ 330int lockfd; /* descriptor of lock file */ 331 332/* global variables for scan functions */ 333int trace = FALSE; /* directory scan trace */ 334int cancompress = FALSE; /* Can we compress files */ 335int docompress = FALSE; /* Do we compress files */ 336 337HASH *uidH[HASHSIZE]; /* for uid and gid lookup */ 338HASH *gidH[HASHSIZE]; 339HASH *inodeH[HASHSIZE]; /* for inode lookup for linked file check */ 340 341 342/* supfilesrv.c */ 343int main(int, char **); 344void chldsig(int); 345void usage(void); 346void init(int, char **); 347void answer(void); 348void srvsignon(void); 349void srvsetup(void); 350void docrypt(void); 351void srvlogin(void); 352void listfiles(void); 353int denyone(TREE *, void *); 354void send_files(void); 355int send_one(TREE *, void *); 356int send_dir(TREE *, void *); 357int send_file(TREE *, va_list); 358void srvfinishup(time_t); 359void Hfree(HASH **); 360HASH *Hlookup(HASH **, int, int); 361void Hinsert(HASH **, int, int, char *, TREE *); 362TREE *linkcheck(TREE *, int, int); 363char *uconvert(int); 364char *gconvert(int); 365char *changeuid(char *, char *, int, int); 366void goaway(const char *, ...); 367char *fmttime(time_t); 368int local_file(int, struct stat *); 369int stat_info_ok(struct stat *, struct stat *); 370int link_nofollow(int); 371int link_nofollow(int); 372 373/************************************* 374 *** M A I N R O U T I N E *** 375 *************************************/ 376 377int 378main(int argc, char **argv) 379{ 380 int x, pid; 381 sigset_t nset, oset; 382 struct sigaction chld, ign; 383 time_t tloc; 384#ifdef LIBWRAP 385 struct request_info req; 386#endif 387 388 /* initialize global variables */ 389 pgmversion = PGMVERSION;/* export version number */ 390 isserver = TRUE; /* export that we're not a server */ 391 collname = NULL; /* no current collection yet */ 392 maxchildren = MAXCHILDREN; /* defined in sup.h */ 393 394 init(argc, argv); /* process arguments */ 395 396#ifdef HAS_DAEMON 397 if (!live) /* if not debugging, turn into daemon */ 398 daemon(0, 0); 399#endif 400 401 logopen("supfile"); 402 tloc = time(NULL); 403 loginfo("SUP File Server Version %d.%d (%s) starting at %s", 404 PROTOVERSION, PGMVERSION, scmversion, fmttime(tloc)); 405 if (live) { 406 x = service(); 407 408 if (x != SCMOK) 409 logquit(1, "Can't connect to network"); 410#ifdef LIBWRAP 411 request_init(&req, RQ_DAEMON, "supfilesrv", RQ_FILE, netfile, 412 NULL); 413 fromhost(&req); 414 if (hosts_access(&req) == 0) { 415 logdeny("refused connection from %.500s", 416 eval_client(&req)); 417 servicekill(); 418 exit(1); 419 } 420 if (sup_clog) { 421 logallow("connection from %.500s", eval_client(&req)); 422 } 423#endif 424 answer(); 425 (void) serviceend(); 426 exit(0); 427 } 428 ign.sa_handler = SIG_IGN; 429 sigemptyset(&ign.sa_mask); 430 ign.sa_flags = 0; 431 (void) sigaction(SIGHUP, &ign, NULL); 432 (void) sigaction(SIGINT, &ign, NULL); 433 (void) sigaction(SIGPIPE, &ign, NULL); 434 chld.sa_handler = chldsig; 435 sigemptyset(&chld.sa_mask); 436 chld.sa_flags = 0; 437 (void) sigaction(SIGCHLD, &chld, NULL); 438 nchildren = 0; 439 for (;;) { 440 x = service(); 441 if (x != SCMOK) { 442 logerr("Error in establishing network connection"); 443 (void) servicekill(); 444 continue; 445 } 446 /* 447 * If we are being bombarded, don't even spend time forking 448 * or conversing 449 */ 450 if (nchildren >= maxchildren + 5) { 451 (void) servicekill(); 452 continue; 453 } 454 sigemptyset(&nset); 455 sigaddset(&nset, SIGCHLD); 456 sigprocmask(SIG_BLOCK, &nset, &oset); 457 if ((pid = fork()) == 0) { /* server process */ 458#ifdef LIBWRAP 459 request_init(&req, RQ_DAEMON, "supfilesrv", RQ_FILE, 460 netfile, NULL); 461 fromhost(&req); 462 if (hosts_access(&req) == 0) { 463 logdeny("refused connection from %.500s", 464 eval_client(&req)); 465 servicekill(); 466 exit(1); 467 } 468 if (sup_clog) { 469 logallow("connection from %.500s", 470 eval_client(&req)); 471 } 472#endif 473 (void) serviceprep(); 474 answer(); 475 (void) serviceend(); 476 exit(0); 477 } 478 (void) servicekill(); /* parent */ 479 if (pid > 0) 480 nchildren++; 481 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 482 } 483} 484/* 485 * Child status signal handler 486 */ 487 488void 489chldsig(int snum __unused) 490{ 491 int w; 492 pid_t pid; 493 494 while ((pid = waitpid(-1, &w, WNOHANG)) > 0) { 495 if (kill(pid, 0) == -1) 496 switch (errno) { 497 case ESRCH: 498 if (nchildren == 0) { 499 logerr("no children but pid %jd\n", 500 (intmax_t)pid); 501 break; 502 } 503 nchildren--; 504 break; 505 default: 506 logerr("killing pid %jd: (%s)\n", (intmax_t) 507 pid, strerror(errno)); 508 break; 509 } 510 511 } 512} 513/***************************************** 514 *** I N I T I A L I Z A T I O N *** 515 *****************************************/ 516 517void 518usage(void) 519{ 520#ifdef LIBWRAP 521 quit(1, "Usage: supfilesrv [ -4 | -6 | -l | -d | -P | -N | -C <max children> | -H <host> <user> <cryptfile> <supargs> ]\n"); 522#else 523 quit(1, "Usage: supfilesrv [ -4 | -6 | -d | -P | -N | -C <max children> | -H <host> <user> <cryptfile> <supargs> ]\n"); 524#endif 525} 526 527void 528init(int argc, char **argv) 529{ 530 int i; 531 int x; 532 char *clienthost, *clientuser; 533 char *p, *q; 534 char buf[STRINGLENGTH]; 535 int maxsleep; 536 FILE *f; 537 int af = AF_INET; 538 539#ifdef RCS 540 candorcs = FALSE; 541#endif 542 live = FALSE; 543#ifdef LIBWRAP 544 sup_clog = FALSE; 545#endif 546 dbgportsq = FALSE; 547 scmdebug = 0; 548 clienthost = NULL; 549 clientuser = NULL; 550 maxsleep = 5; 551 if (--argc < 0) 552 usage(); 553 argv++; 554 while (clienthost == NULL && argc > 0 && argv[0][0] == '-') { 555 switch (argv[0][1]) { 556 case 'S': 557 silent = TRUE; 558 break; 559#ifdef LIBWRAP 560 case 'l': 561 sup_clog = TRUE; 562 break; 563#endif 564 case 'd': 565 live = TRUE; 566 break; 567 case 'P': 568 dbgportsq = TRUE; 569 break; 570 case 'N': 571 scmdebug++; 572 break; 573 case 'C': 574 if (--argc < 1) 575 quit(1, "Missing arg to -C\n"); 576 argv++; 577 maxchildren = atoi(argv[0]); 578 break; 579 case 'H': 580 if (--argc < 3) 581 quit(1, "Missing args to -H\n"); 582 argv++; 583 clienthost = argv[0]; 584 clientuser = argv[1]; 585 cryptkey = argv[2]; 586 argc -= 2; 587 argv += 2; 588 break; 589#ifdef RCS 590 case 'R': 591 candorcs = TRUE; 592 break; 593#endif 594 case '4': 595 af = AF_INET; 596 break; 597#ifdef AF_INET6 598 case '6': 599 af = AF_INET6; 600 break; 601#endif 602 default: 603 fprintf(stderr, "Unknown flag %s ignored\n", argv[0]); 604 break; 605 } 606 --argc; 607 argv++; 608 } 609 if (clienthost == NULL) { 610 if (argc != 0) 611 usage(); 612 x = servicesetup(dbgportsq ? DEBUGFPORT : FILEPORT, af); 613 if (x != SCMOK) 614 quit(1, "Error in network setup"); 615 for (i = 0; i < HASHSIZE; i++) 616 uidH[i] = gidH[i] = inodeH[i] = NULL; 617 return; 618 } 619 isserver = FALSE; 620 if (argc < 1) 621 usage(); 622 f = fopen(cryptkey, "r"); 623 if (f == NULL) 624 quit(1, "Unable to open cryptfile %s\n", cryptkey); 625 if ((p = fgets(buf, STRINGLENGTH, f)) != NULL) { 626 if ((q = strchr(p, '\n')) != NULL) 627 *q = '\0'; 628 if (*p == '\0') 629 quit(1, "No cryptkey found in %s\n", cryptkey); 630 cryptkey = estrdup(buf); 631 } 632 (void) fclose(f); 633 x = request(dbgportsq ? DEBUGFPORT : FILEPORT, clienthost, &maxsleep); 634 if (x != SCMOK) 635 quit(1, "Unable to connect to host %s\n", clienthost); 636 x = msgsignon(); 637 if (x != SCMOK) 638 quit(1, "Error sending signon request to fileserver\n"); 639 x = msgsignonack(); 640 if (x != SCMOK) 641 quit(1, "Error reading signon reply from fileserver\n"); 642 printf("SUP Fileserver %d.%d (%s) %d on %s\n", 643 protver, pgmver, scmver, fspid, remotehost()); 644 free(scmver); 645 scmver = NULL; 646 if (protver < 7) 647 quit(1, "Remote fileserver does not implement reverse sup\n"); 648 xpatch = TRUE; 649 xuser = clientuser; 650 x = msgsetup(); 651 if (x != SCMOK) 652 quit(1, "Error sending setup request to fileserver\n"); 653 x = msgsetupack(); 654 if (x != SCMOK) 655 quit(1, "Error reading setup reply from fileserver\n"); 656 switch (setupack) { 657 case FSETUPOK: 658 break; 659 case FSETUPSAME: 660 quit(1, "User %s not found on remote client\n", xuser); 661 case FSETUPHOST: 662 quit(1, "This host has no permission to reverse sup\n"); 663 default: 664 quit(1, "Unrecognized file server setup status %d\n", setupack); 665 } 666 if (netcrypt(cryptkey) != SCMOK) 667 quit(1, "Running non-crypting fileserver\n"); 668 crypttest = CRYPTTEST; 669 x = msgcrypt(); 670 if (x != SCMOK) 671 quit(1, "Error sending encryption test request\n"); 672 x = msgcryptok(); 673 if (x == SCMEOF) 674 quit(1, "Data encryption test failed\n"); 675 if (x != SCMOK) 676 quit(1, "Error reading encryption test reply\n"); 677 logcrypt = CRYPTTEST; 678 loguser = NULL; 679 logpswd = NULL; 680 if (netcrypt(PSWDCRYPT) != SCMOK) /* encrypt password data */ 681 quit(1, "Running non-crypting fileserver\n"); 682 x = msglogin(); 683 (void) netcrypt(NULL); /* turn off encryption */ 684 if (x != SCMOK) 685 quit(1, "Error sending login request to file server\n"); 686 x = msglogack(); 687 if (x != SCMOK) 688 quit(1, "Error reading login reply from file server\n"); 689 if (logack == FLOGNG) 690 quit(1, "%s\nImproper login to %s account\n", logerror, xuser); 691 xargc = argc; 692 xargv = argv; 693 x = msgxpatch(); 694 if (x != SCMOK) 695 quit(1, "Error sending crosspatch request\n"); 696 crosspatch(); 697 exit(0); 698} 699/***************************************** 700 *** A N S W E R R E Q U E S T *** 701 *****************************************/ 702 703void 704answer(void) 705{ 706 time_t starttime; 707 int x; 708 709 progpid = fspid = getpid(); 710 collname = NULL; 711 basedir = NULL; 712 prefix = NULL; 713 release = NULL; 714 rcs_branch = NULL; 715#ifdef CVS 716 cvs_root = NULL; 717#endif 718 goawayreason = NULL; 719 donereason = NULL; 720 lockfd = -1; 721 starttime = time(NULL); 722 if (!setjmp(sjbuf)) { 723 srvsignon(); 724 srvsetup(); 725 docrypt(); 726 srvlogin(); 727 if (xpatch) { 728 int fd; 729 730 x = msgxpatch(); 731 if (x != SCMOK) 732 exit(0); 733 xargv[0] = "sup"; 734 xargv[1] = "-X"; 735 xargv[xargc] = NULL; 736 (void) dup2(netfile, 0); 737 (void) dup2(netfile, 1); 738 (void) dup2(netfile, 2); 739 fd = getdtablesize(); 740 while (--fd > 2) 741 (void) close(fd); 742 execvp(xargv[0], xargv); 743 exit(0); 744 } 745 listfiles(); 746 send_files(); 747 } 748 srvfinishup(starttime); 749 if (collname) 750 free(collname); 751 if (basedir) 752 free(basedir); 753 if (prefix) 754 free(prefix); 755 if (release) 756 free(release); 757 if (rcs_branch) 758 free(rcs_branch); 759#ifdef CVS 760 if (cvs_root) 761 free(cvs_root); 762#endif 763 if (goawayreason) { 764 if (donereason == goawayreason) 765 donereason = NULL; 766 free(goawayreason); 767 } 768 if (donereason) 769 free(donereason); 770 if (lockfd >= 0) 771 (void) close(lockfd); 772 endpwent(); 773 (void) endgrent(); 774#if CMUCS 775 endacent(); 776#endif /* CMUCS */ 777 Hfree(uidH); 778 Hfree(gidH); 779 Hfree(inodeH); 780} 781/***************************************** 782 *** S I G N O N C L I E N T *** 783 *****************************************/ 784 785void 786srvsignon(void) 787{ 788 int x; 789 790 xpatch = FALSE; 791 x = msgsignon(); 792 if (x != SCMOK) 793 goaway("Error reading signon request from client"); 794 x = msgsignonack(); 795 if (x != SCMOK) 796 goaway("Error sending signon reply to client"); 797 free(scmver); 798 scmver = NULL; 799} 800/***************************************************************** 801 *** E X C H A N G E S E T U P I N F O R M A T I O N *** 802 *****************************************************************/ 803 804void 805srvsetup(void) 806{ 807 int x; 808 char *p, *q; 809 char buf[STRINGLENGTH], filename[MAXPATHLEN]; 810 FILE *f; 811 struct stat sbuf; 812 TREELIST *tl; 813 814 if (protver > 7) { 815 cancompress = TRUE; 816 } 817 x = msgsetup(); 818 if (x != SCMOK) 819 goaway("Error reading setup request from client"); 820 if (protver < 4) { 821 setupack = FSETUPOLD; 822 (void) msgsetupack(); 823 if (protver >= 6) 824 longjmp(sjbuf, TRUE); 825 goaway("Sup client using obsolete version of protocol"); 826 } 827 if (xpatch) { 828 struct passwd *pw; 829 830 if ((pw = getpwnam(xuser)) == NULL) { 831 setupack = FSETUPSAME; 832 (void) msgsetupack(); 833 if (protver >= 6) 834 longjmp(sjbuf, TRUE); 835 goaway("User `%s' not found", xuser); 836 } 837 (void) free(xuser); 838 xuser = estrdup(pw->pw_dir); 839 840 /* check crosspatch host access file */ 841 cryptkey = NULL; 842 (void) sprintf(buf, FILEXPATCH, xuser); 843 844 /* Turn off link following */ 845 if (link_nofollow(1) != -1) { 846 int hostok = FALSE; 847 /* get stat info before open */ 848 if (stat(buf, &sbuf) == -1) 849 (void) bzero(&sbuf, sizeof(sbuf)); 850 851 if ((f = fopen(buf, "r")) != NULL) { 852 struct stat fsbuf; 853 854 while ((p = fgets(buf, STRINGLENGTH, f)) != NULL) { 855 q = strchr(p, '\n'); 856 if (q) 857 *q = 0; 858 if (strchr("#;:", *p)) 859 continue; 860 q = nxtarg(&p, " \t"); 861 if (*p == '\0') 862 continue; 863 if (!matchhost(q)) 864 continue; 865 866 cryptkey = estrdup(p); 867 hostok = TRUE; 868 if (local_file(fileno(f), &fsbuf) > 0 869 && stat_info_ok(&sbuf, &fsbuf)) { 870 runas_uid = sbuf.st_uid; 871 runas_gid = sbuf.st_gid; 872 } 873 break; 874 } 875 (void) fclose(f); 876 } 877 /* Restore link following */ 878 if (link_nofollow(0) == -1) 879 goaway("Restore link following"); 880 881 if (!hostok) { 882 setupack = FSETUPHOST; 883 (void) msgsetupack(); 884 if (protver >= 6) 885 longjmp(sjbuf, TRUE); 886 goaway("Host not on access list"); 887 } 888 } 889 setupack = FSETUPOK; 890 x = msgsetupack(); 891 if (x != SCMOK) 892 goaway("Error sending setup reply to client"); 893 return; 894 } 895#ifdef RCS 896 if (candorcs && release != NULL && 897 (strncmp(release, "RCS.", 4) == 0)) { 898 rcs_branch = estrdup(&release[4]); 899 free(release); 900 release = estrdup("RCS"); 901 dorcs = TRUE; 902 } 903#endif 904 if (release == NULL) 905 release = estrdup(DEFRELEASE); 906 if (basedir == NULL || *basedir == '\0') { 907 basedir = NULL; 908 (void) sprintf(filename, FILEDIRS, DEFDIR); 909 f = fopen(filename, "r"); 910 if (f) { 911 while ((p = fgets(buf, STRINGLENGTH, f)) != NULL) { 912 q = strchr(p, '\n'); 913 if (q) 914 *q = 0; 915 if (strchr("#;:", *p)) 916 continue; 917 q = nxtarg(&p, " \t="); 918 if (strcmp(q, collname) == 0) { 919 basedir = skipover(p, " \t="); 920 basedir = estrdup(basedir); 921 break; 922 } 923 } 924 (void) fclose(f); 925 } 926 if (basedir == NULL) { 927 (void) sprintf(buf, FILEBASEDEFAULT, collname); 928 basedir = estrdup(buf); 929 } 930 } 931 if (chdir(basedir) < 0) 932 goaway("Can't chdir to base directory %s (%s)", basedir, 933 strerror(errno)); 934 (void) sprintf(filename, FILEPREFIX, collname); 935 f = fopen(filename, "r"); 936 if (f) { 937 while ((p = fgets(buf, STRINGLENGTH, f)) != NULL) { 938 q = strchr(p, '\n'); 939 if (q) 940 *q = 0; 941 if (strchr("#;:", *p)) 942 continue; 943 prefix = estrdup(p); 944 if (chdir(prefix) < 0) 945 goaway("%s: Can't chdir to %s from base " 946 "directory %s (%s)", filename, prefix, 947 basedir, strerror(errno)); 948 break; 949 } 950 (void) fclose(f); 951 } 952 x = stat(".", &sbuf); 953 if (prefix) 954 (void) chdir(basedir); 955 if (x < 0) 956 goaway("Can't stat base/prefix directory (%s)", 957 strerror(errno)); 958 if (nchildren >= maxchildren) { 959 setupack = FSETUPBUSY; 960 (void) msgsetupack(); 961 if (protver >= 6) 962 longjmp(sjbuf, TRUE); 963 goaway("Sup client told to try again later"); 964 } 965 if (sbuf.st_dev == basedev && sbuf.st_ino == baseino && samehost()) { 966 setupack = FSETUPSAME; 967 (void) msgsetupack(); 968 if (protver >= 6) 969 longjmp(sjbuf, TRUE); 970 goaway("Attempt to upgrade to same directory on same host"); 971 } 972 /* obtain release information */ 973 if (!getrelease(release)) { 974 setupack = FSETUPRELEASE; 975 (void) msgsetupack(); 976 if (protver >= 6) 977 longjmp(sjbuf, TRUE); 978 goaway("Invalid release information"); 979 } 980 /* check host access file */ 981 cryptkey = NULL; 982 for (tl = listTL; tl != NULL; tl = tl->TLnext) { 983 char *h; 984 if ((h = tl->TLhost) == NULL) 985 h = FILEHOSTDEF; 986 (void) sprintf(buf, FILEHOST, collname, h); 987 f = fopen(buf, "r"); 988 if (f) { 989 int hostok = FALSE; 990 while ((p = fgets(buf, STRINGLENGTH, f)) != NULL) { 991 int not; 992 q = strchr(p, '\n'); 993 if (q) 994 *q = 0; 995 if (strchr("#;:", *p)) 996 continue; 997 q = nxtarg(&p, " \t"); 998 if ((not = (*q == '!')) && *++q == '\0') 999 q = nxtarg(&p, " \t"); 1000 hostok = (not == (matchhost(q) == 0)); 1001 if (hostok) { 1002 while ((*p == ' ') || (*p == '\t')) 1003 p++; 1004 if (*p) 1005 cryptkey = estrdup(p); 1006 break; 1007 } 1008 } 1009 (void) fclose(f); 1010 if (!hostok) { 1011 setupack = FSETUPHOST; 1012 (void) msgsetupack(); 1013 if (protver >= 6) 1014 longjmp(sjbuf, TRUE); 1015 goaway("Host not on access list for %s", 1016 collname); 1017 } 1018 } 1019 } 1020 /* try to lock collection */ 1021 (void) sprintf(buf, FILELOCK, collname); 1022#ifdef LOCK_SH 1023 x = open(buf, O_RDONLY, 0); 1024 if (x >= 0) { 1025 if (flock(x, (LOCK_SH | LOCK_NB)) < 0) { 1026 (void) close(x); 1027 if (errno != EWOULDBLOCK) 1028 goaway("Can't lock collection %s", collname); 1029 setupack = FSETUPBUSY; 1030 (void) msgsetupack(); 1031 if (protver >= 6) 1032 longjmp(sjbuf, TRUE); 1033 goaway("Sup client told to wait for lock"); 1034 } 1035 lockfd = x; 1036 } 1037#endif 1038 setupack = FSETUPOK; 1039 x = msgsetupack(); 1040 if (x != SCMOK) 1041 goaway("Error sending setup reply to client"); 1042} 1043 1044void 1045/** Test data encryption **/ 1046docrypt(void) 1047{ 1048 int x; 1049 char *p, *q; 1050 char buf[STRINGLENGTH]; 1051 FILE *f; 1052 struct stat sbuf; 1053 1054 if (!xpatch) { 1055 (void) sprintf(buf, FILECRYPT, collname); 1056 1057 /* Turn off link following */ 1058 if (link_nofollow(1) != -1) { 1059 /* get stat info before open */ 1060 if (stat(buf, &sbuf) == -1) 1061 (void) bzero(&sbuf, sizeof(sbuf)); 1062 1063 if ((f = fopen(buf, "r")) != NULL) { 1064 struct stat fsbuf; 1065 1066 if (cryptkey == NULL && 1067 (p = fgets(buf, STRINGLENGTH, f))) { 1068 if ((q = strchr(p, '\n')) != NULL) 1069 *q = '\0'; 1070 if (*p) 1071 cryptkey = estrdup(buf); 1072 } 1073 if (local_file(fileno(f), &fsbuf) > 0 1074 && stat_info_ok(&sbuf, &fsbuf)) { 1075 runas_uid = sbuf.st_uid; 1076 runas_gid = sbuf.st_gid; 1077 } 1078 (void) fclose(f); 1079 } 1080 /* Restore link following */ 1081 if (link_nofollow(0) == -1) 1082 goaway("Restore link following"); 1083 } 1084 } 1085 if (netcrypt(cryptkey) != SCMOK) 1086 goaway("Runing non-crypting supfilesrv"); 1087 x = msgcrypt(); 1088 if (x != SCMOK) 1089 goaway("Error reading encryption test request from client"); 1090 (void) netcrypt(NULL); 1091 if (strcmp(crypttest, CRYPTTEST) != 0) 1092 goaway("Client not encrypting data properly"); 1093 free(crypttest); 1094 crypttest = NULL; 1095 x = msgcryptok(); 1096 if (x != SCMOK) 1097 goaway("Error sending encryption test reply to client"); 1098} 1099/*************************************************************** 1100 *** C O N N E C T T O P R O P E R A C C O U N T *** 1101 ***************************************************************/ 1102 1103void 1104srvlogin(void) 1105{ 1106 int x, fileuid = -1, filegid = -1; 1107 1108 (void) netcrypt(PSWDCRYPT); /* encrypt acct name and password */ 1109 x = msglogin(); 1110 (void) netcrypt(NULL); /* turn off encryption */ 1111 if (x != SCMOK) 1112 goaway("Error reading login request from client"); 1113 if (logcrypt) { 1114 if (strcmp(logcrypt, CRYPTTEST) != 0) { 1115 logack = FLOGNG; 1116 logerror = "Improper login encryption"; 1117 (void) msglogack(); 1118 goaway("Client not encrypting login information properly"); 1119 } 1120 free(logcrypt); 1121 logcrypt = NULL; 1122 } 1123 if (loguser == NULL) { 1124 if (cryptkey) { 1125 if (runas_uid >= 0 && runas_gid >= 0) { 1126 fileuid = runas_uid; 1127 filegid = runas_gid; 1128 loguser = NULL; 1129 } else 1130 loguser = estrdup(DEFUSER); 1131 } else 1132 loguser = estrdup(DEFUSER); 1133 } 1134 if ((logerror = changeuid(loguser, logpswd, fileuid, filegid)) != NULL) { 1135 logack = FLOGNG; 1136 (void) msglogack(); 1137 if (protver >= 6) 1138 longjmp(sjbuf, TRUE); 1139 goaway("Client denied login access"); 1140 } 1141 if (loguser) 1142 free(loguser); 1143 if (logpswd) 1144 free(logpswd); 1145 logack = FLOGOK; 1146 x = msglogack(); 1147 if (x != SCMOK) 1148 goaway("Error sending login reply to client"); 1149 if (!xpatch) /* restore desired encryption */ 1150 if (netcrypt(cryptkey) != SCMOK) 1151 goaway("Running non-crypting supfilesrv"); 1152 free(cryptkey); 1153 cryptkey = NULL; 1154} 1155/***************************************** 1156 *** M A K E N A M E L I S T *** 1157 *****************************************/ 1158 1159void 1160listfiles(void) 1161{ 1162 int x; 1163 1164 refuseT = NULL; 1165 x = msgrefuse(); 1166 if (x != SCMOK) 1167 goaway("Error reading refuse list from client"); 1168 getscanlists(); 1169 Tfree(&refuseT); 1170 x = msglist(); 1171 if (x != SCMOK) 1172 goaway("Error sending file list to client"); 1173 Tfree(&listT); 1174 listT = NULL; 1175 needT = NULL; 1176 x = msgneed(); 1177 if (x != SCMOK) 1178 goaway("Error reading needed files list from client"); 1179 denyT = NULL; 1180 (void) Tprocess(needT, denyone, NULL); 1181 Tfree(&needT); 1182 x = msgdeny(); 1183 if (x != SCMOK) 1184 goaway("Error sending denied files list to client"); 1185 Tfree(&denyT); 1186} 1187 1188 1189int 1190denyone(TREE * t, void *v __unused) 1191{ 1192 TREELIST *tl; 1193 char *name = t->Tname; 1194 int update = (t->Tflags & FUPDATE) != 0; 1195 struct stat sbuf; 1196 TREE *tlink; 1197 char slinkname[STRINGLENGTH]; 1198 int x; 1199 1200 for (tl = listTL; tl != NULL; tl = tl->TLnext) 1201 if ((t = Tsearch(tl->TLtree, name)) != NULL) 1202 break; 1203 if (t == NULL) { 1204 (void) Tinsert(&denyT, name, FALSE); 1205 return (SCMOK); 1206 } 1207 cdprefix(tl->TLprefix); 1208 if (S_ISLNK(t->Tmode)) 1209 x = lstat(name, &sbuf); 1210 else 1211 x = stat(name, &sbuf); 1212 if (x < 0 || (sbuf.st_mode & S_IFMT) != (t->Tmode & S_IFMT)) { 1213 (void) Tinsert(&denyT, name, FALSE); 1214 return (SCMOK); 1215 } 1216 switch (t->Tmode & S_IFMT) { 1217 case S_IFLNK: 1218 if ((x = readlink(name, slinkname, STRINGLENGTH - 1)) <= 0) { 1219 (void) Tinsert(&denyT, name, FALSE); 1220 return (SCMOK); 1221 } 1222 slinkname[x] = '\0'; 1223 (void) Tinsert(&t->Tlink, slinkname, FALSE); 1224 break; 1225 case S_IFREG: 1226 if (sbuf.st_nlink > 1 && 1227 (tlink = linkcheck(t, (int) sbuf.st_dev, (int) sbuf.st_ino))) { 1228 (void) Tinsert(&tlink->Tlink, name, FALSE); 1229 return (SCMOK); 1230 } 1231 if (update) 1232 t->Tflags |= FUPDATE; 1233 case S_IFDIR: 1234 t->Tuid = sbuf.st_uid; 1235 t->Tgid = sbuf.st_gid; 1236 break; 1237 default: 1238 (void) Tinsert(&denyT, name, FALSE); 1239 return (SCMOK); 1240 } 1241 t->Tflags |= FNEEDED; 1242 return (SCMOK); 1243} 1244/********************************* 1245 *** S E N D F I L E S *** 1246 *********************************/ 1247 1248void 1249send_files(void) 1250{ 1251 TREELIST *tl; 1252 int x; 1253 1254 /* Does the protocol support compression */ 1255 if (cancompress) { 1256 /* Check for compression on sending files */ 1257 x = msgcompress(); 1258 if (x != SCMOK) 1259 goaway("Error sending compression check to server"); 1260 } 1261 /* send all files */ 1262 for (tl = listTL; tl != NULL; tl = tl->TLnext) { 1263 cdprefix(tl->TLprefix); 1264#ifdef CVS 1265 if (candorcs) { 1266 cvs_root = getcwd(NULL, 256); 1267 if (access("CVSROOT", F_OK) < 0) 1268 dorcs = FALSE; 1269 else { 1270 loginfo("is a CVSROOT \"%s\"\n", cvs_root); 1271 dorcs = TRUE; 1272 } 1273 } 1274#endif 1275 (void) Tprocess(tl->TLtree, send_one, NULL); 1276 } 1277 /* send directories in reverse order */ 1278 for (tl = listTL; tl != NULL; tl = tl->TLnext) { 1279 cdprefix(tl->TLprefix); 1280 (void) Trprocess(tl->TLtree, send_dir, NULL); 1281 } 1282 x = msgsend(); 1283 if (x != SCMOK) 1284 goaway("Error reading receive file request from client"); 1285 upgradeT = NULL; 1286 x = msgrecv(send_file, 0); 1287 if (x != SCMOK) 1288 goaway("Error sending file to client"); 1289} 1290 1291int 1292send_one(TREE * t, void *v __unused) 1293{ 1294 int x, fd; 1295 char temp_file[STRINGLENGTH]; 1296 char *av[50]; /* More than enough */ 1297 1298 if ((t->Tflags & FNEEDED) == 0) /* only send needed files */ 1299 return (SCMOK); 1300 if (S_ISDIR(t->Tmode)) /* send no directories this pass */ 1301 return (SCMOK); 1302 x = msgsend(); 1303 if (x != SCMOK) 1304 goaway("Error reading receive file request from client"); 1305 upgradeT = t; /* upgrade file pointer */ 1306 fd = -1; /* no open file */ 1307 if (S_ISREG(t->Tmode)) { 1308 if (!listonly && (t->Tflags & FUPDATE) == 0) { 1309#ifdef RCS 1310 if (dorcs) { 1311 char rcs_release[STRINGLENGTH]; 1312 1313 tmpnam(rcs_file); 1314 fd = open(rcs_file, (O_WRONLY | O_CREAT | O_TRUNC | O_EXCL), 0600); 1315 if (fd < 0) 1316 goaway("We died trying to create temp file"); 1317 close(fd); 1318 fd = -1; 1319 if (strcmp(&t->Tname[strlen(t->Tname) - 2], ",v") == 0) { 1320 t->Tname[strlen(t->Tname) - 2] = '\0'; 1321 ac = 0; 1322#ifdef CVS 1323 av[ac++] = "cvs"; 1324 av[ac++] = "-d"; 1325 av[ac++] = cvs_root; 1326 av[ac++] = "-r"; 1327 av[ac++] = "-l"; 1328 av[ac++] = "-Q"; 1329 av[ac++] = "co"; 1330 av[ac++] = "-p"; 1331 if (rcs_branch != NULL) { 1332 av[ac++] = "-r"; 1333 av[ac++] = rcs_branch; 1334 } 1335#else 1336 av[ac++] = "co"; 1337 av[ac++] = "-q"; 1338 av[ac++] = "-p"; 1339 if (rcs_branch != NULL) { 1340 sprintf(rcs_release, "-r%s", 1341 rcs_branch); 1342 av[ac++] = rcs_release; 1343 } 1344#endif 1345 av[ac++] = t->Tname; 1346 av[ac++] = NULL; 1347 status = runio(av, NULL, rcs_file, 1348 "/dev/null"); 1349 /* loginfo("using rcs mode \n"); */ 1350 if (status < 0 || WEXITSTATUS(status)) { 1351 /* Just in case */ 1352 unlink(rcs_file); 1353 if (status < 0) { 1354 goaway("We died trying to run cvs or rcs on %s", rcs_file); 1355 t->Tmode = 0; 1356 } else { 1357#if 0 1358 logerr("rcs command failed = %d\n", 1359 WEXITSTATUS(status)); 1360#endif 1361 t->Tflags |= FUPDATE; 1362 } 1363 } else if (docompress) { 1364 tmpnam(temp_file); 1365 av[0] = "gzip"; 1366 av[1] = "-cf"; 1367 av[2] = NULL; 1368 if (runio(av, rcs_file, temp_file, NULL) != 0) { 1369 /* Just in case */ 1370 unlink(temp_file); 1371 unlink(rcs_file); 1372 goaway("We died trying to gzip %s", rcs_file); 1373 t->Tmode = 0; 1374 } 1375 fd = open(temp_file, O_RDONLY, 0); 1376 } else 1377 fd = open(rcs_file, O_RDONLY, 0); 1378 } 1379 } 1380#endif 1381 if (fd == -1) { 1382 if (docompress) { 1383 snprintf(temp_file, sizeof(temp_file), 1384 "%s/supfilesrv.XXXXXX", P_tmpdir); 1385 fd = mkstemp(temp_file); 1386 if (fd < 0) 1387 goaway("We died trying to create temp file"); 1388 close(fd); 1389 fd = -1; 1390 av[0] = "gzip"; 1391 av[1] = "-cf"; 1392 av[2] = NULL; 1393 if (runio(av, t->Tname, temp_file, NULL) != 0) { 1394 /* Just in case */ 1395 unlink(temp_file); 1396 goaway("We died trying to gzip %s", t->Tname); 1397 t->Tmode = 0; 1398 } 1399 fd = open(temp_file, O_RDONLY, 0); 1400 } else 1401 fd = open(t->Tname, O_RDONLY, 0); 1402 } 1403 if (fd < 0 && (t->Tflags & FUPDATE) == 0) 1404 t->Tmode = 0; 1405 } 1406 if (t->Tmode) { 1407 t->Tuser = estrdup(uconvert(t->Tuid)); 1408 t->Tgroup = estrdup(gconvert(t->Tgid)); 1409 } 1410 } 1411 x = msgrecv(send_file, fd); 1412 if (docompress) 1413 unlink(temp_file); 1414#ifdef RCS 1415 if (dorcs) 1416 unlink(rcs_file); 1417#endif 1418 if (x != SCMOK) 1419 goaway("Error sending file %s to client", t->Tname); 1420 return (SCMOK); 1421} 1422 1423int 1424send_dir(TREE * t, void *v __unused) 1425{ 1426 int x; 1427 1428 if ((t->Tflags & FNEEDED) == 0) /* only send needed files */ 1429 return (SCMOK); 1430 if (!S_ISDIR(t->Tmode)) /* send only directories this pass */ 1431 return (SCMOK); 1432 x = msgsend(); 1433 if (x != SCMOK) 1434 goaway("Error reading receive file request from client"); 1435 upgradeT = t; /* upgrade file pointer */ 1436 t->Tuser = estrdup(uconvert(t->Tuid)); 1437 t->Tgroup = estrdup(gconvert(t->Tgid)); 1438 x = msgrecv(send_file, 0); 1439 if (x != SCMOK) 1440 goaway("Error sending file %s to client", t->Tname); 1441 return (SCMOK); 1442} 1443 1444int 1445send_file(TREE * t, va_list ap) 1446{ 1447 int x, fd; 1448 1449 fd = va_arg(ap, int); 1450 if (!S_ISREG(t->Tmode) || listonly || (t->Tflags & FUPDATE)) 1451 return (SCMOK); 1452 x = writefile(fd); 1453 if (x != SCMOK) 1454 goaway("Error sending file %s to client", t->Tname); 1455 (void) close(fd); 1456 return (SCMOK); 1457} 1458/***************************************** 1459 *** E N D C O N N E C T I O N *** 1460 *****************************************/ 1461 1462void 1463srvfinishup(time_t starttime) 1464{ 1465 int x = SCMOK; 1466 char tmpbuf[BUFSIZ], *p, lognam[STRINGLENGTH]; 1467 int logfd; 1468 time_t finishtime; 1469 char *releasename; 1470 1471 (void) netcrypt(NULL); 1472 if (protver < 6) { 1473 if (goawayreason != NULL) 1474 free(goawayreason); 1475 goawayreason = NULL; 1476 x = msggoaway(); 1477 doneack = FDONESUCCESS; 1478 donereason = estrdup("Unknown"); 1479 } else if (goawayreason == NULL) 1480 x = msgdone(); 1481 else { 1482 doneack = FDONEGOAWAY; 1483 donereason = goawayreason; 1484 } 1485 if (x == SCMEOF || x == SCMERR) { 1486 doneack = FDONEUSRERROR; 1487 donereason = estrdup("Premature EOF on network"); 1488 } else if (x != SCMOK) { 1489 doneack = FDONESRVERROR; 1490 donereason = estrdup("Unknown SCM code"); 1491 } 1492 if (doneack == FDONEDONTLOG) 1493 return; 1494 if (donereason == NULL) 1495 donereason = estrdup("No reason"); 1496 if (doneack == FDONESRVERROR || doneack == FDONEUSRERROR) 1497 logerr("%s: %s", remotehost(), donereason); 1498 else if (doneack == FDONEGOAWAY) 1499 logerr("GOAWAY: %s: %s", remotehost(), donereason); 1500 else if (doneack != FDONESUCCESS) 1501 logerr("%s: Reason %d: %s", remotehost(), doneack, donereason); 1502 goawayreason = donereason; 1503 cdprefix(NULL); 1504 if (collname == NULL) { 1505 logerr("%s: NULL collection in svrfinishup", remotehost()); 1506 return; 1507 } 1508 (void) sprintf(lognam, FILELOGFILE, collname); 1509 if ((logfd = open(lognam, O_APPEND | O_WRONLY, 0644)) < 0) 1510 return; /* can not open file up...error */ 1511 finishtime = time(NULL); 1512 p = tmpbuf; 1513 (void) sprintf(p, "%s ", fmttime(lasttime)); 1514 p += strlen(p); 1515 (void) sprintf(p, "%s ", fmttime(starttime)); 1516 p += strlen(p); 1517 (void) sprintf(p, "%s ", fmttime(finishtime)); 1518 p += strlen(p); 1519 if ((releasename = release) == NULL) 1520 releasename = "UNKNOWN"; 1521 (void) sprintf(p, "%s %s %d %s\n", remotehost(), releasename, 1522 FDONESUCCESS - doneack, donereason); 1523 p += strlen(p); 1524#if MACH 1525 /* if we are busy dont get stuck updating the disk if full */ 1526 if (setupack == FSETUPBUSY) { 1527 long l = FIOCNOSPC_ERROR; 1528 ioctl(logfd, FIOCNOSPC, &l); 1529 } 1530#endif /* MACH */ 1531 (void) write(logfd, tmpbuf, (p - tmpbuf)); 1532 (void) close(logfd); 1533} 1534/*************************************************** 1535 *** H A S H T A B L E R O U T I N E S *** 1536 ***************************************************/ 1537 1538void 1539Hfree(HASH ** table) 1540{ 1541 HASH *h; 1542 int i; 1543 for (i = 0; i < HASHSIZE; i++) 1544 while ((h = table[i]) != NULL) { 1545 table[i] = h->Hnext; 1546 if (h->Hname) 1547 free(h->Hname); 1548 free(h); 1549 } 1550} 1551 1552HASH * 1553Hlookup(HASH ** table, int num1, int num2) 1554{ 1555 HASH *h; 1556 int hno; 1557 hno = HASHFUNC(num1, num2); 1558 for (h = table[hno]; h && (h->Hnum1 != num1 || h->Hnum2 != num2); h = h->Hnext); 1559 return (h); 1560} 1561 1562void 1563Hinsert(HASH ** table, int num1, int num2, char *name, TREE * tree) 1564{ 1565 HASH *h; 1566 int hno; 1567 hno = HASHFUNC(num1, num2); 1568 h = (HASH *) malloc(sizeof(HASH)); 1569 if (h == NULL) 1570 goaway("Cannot allocate memory"); 1571 h->Hnum1 = num1; 1572 h->Hnum2 = num2; 1573 h->Hname = name; 1574 h->Htree = tree; 1575 h->Hnext = table[hno]; 1576 table[hno] = h; 1577} 1578/********************************************* 1579 *** U T I L I T Y R O U T I N E S *** 1580 *********************************************/ 1581 1582TREE * 1583linkcheck(TREE * t, int d, int i) 1584 /* inode # and device # */ 1585{ 1586 HASH *h; 1587 h = Hlookup(inodeH, i, d); 1588 if (h) 1589 return (h->Htree); 1590 Hinsert(inodeH, i, d, NULL, t); 1591 return (NULL); 1592} 1593 1594char * 1595uconvert(int uid) 1596{ 1597 struct passwd *pw; 1598 char *p; 1599 HASH *u; 1600 u = Hlookup(uidH, uid, 0); 1601 if (u) 1602 return (u->Hname); 1603 pw = getpwuid(uid); 1604 if (pw == NULL) 1605 return (""); 1606 p = estrdup(pw->pw_name); 1607 Hinsert(uidH, uid, 0, p, NULL); 1608 return (p); 1609} 1610 1611char * 1612gconvert(int gid) 1613{ 1614 struct group *gr; 1615 char *p; 1616 HASH *g; 1617 g = Hlookup(gidH, gid, 0); 1618 if (g) 1619 return (g->Hname); 1620 gr = getgrgid(gid); 1621 if (gr == NULL) 1622 return (""); 1623 p = estrdup(gr->gr_name); 1624 Hinsert(gidH, gid, 0, p, NULL); 1625 return (p); 1626} 1627 1628char * 1629changeuid(char *namep, char *passwordp, int fileuid, int filegid) 1630{ 1631 char *group, *account, *pswdp; 1632 struct passwd *pwd; 1633 struct group *grp; 1634#if CMUCS 1635 struct account *acc; 1636 struct ttyloc tlc; 1637#endif /* CMUCS */ 1638 int status = ACCESS_CODE_OK; 1639 char nbuf[STRINGLENGTH]; 1640 static char errbuf[STRINGLENGTH]; 1641#if CMUCS 1642 int *grps; 1643#endif /* CMUCS */ 1644 char *p = NULL; 1645 1646 if (namep == NULL) { 1647 pwd = getpwuid(fileuid); 1648 if (pwd == NULL) { 1649 (void) sprintf(errbuf, "Reason: Unknown user id %d", 1650 fileuid); 1651 return (errbuf); 1652 } 1653 grp = getgrgid(filegid); 1654 if (grp) 1655 group = strcpy(nbuf, grp->gr_name); 1656 else 1657 group = NULL; 1658 account = NULL; 1659 pswdp = NULL; 1660 } else { 1661 (void) strcpy(nbuf, namep); 1662 account = group = strchr(nbuf, ','); 1663 if (group != NULL) { 1664 *group++ = '\0'; 1665 account = strchr(group, ','); 1666 if (account != NULL) { 1667 *account++ = '\0'; 1668 if (*account == '\0') 1669 account = NULL; 1670 } 1671 if (*group == '\0') 1672 group = NULL; 1673 } 1674 pwd = getpwnam(nbuf); 1675 if (pwd == NULL) { 1676 (void) sprintf(errbuf, "Reason: Unknown user %s", 1677 nbuf); 1678 return (errbuf); 1679 } 1680 if (strcmp(nbuf, DEFUSER) == 0) 1681 pswdp = NULL; 1682 else 1683 pswdp = passwordp ? passwordp : ""; 1684#ifdef AFS 1685 if (strcmp(nbuf, DEFUSER) != 0) { 1686 char *reason; 1687 setpag(); /* set a pag */ 1688 if (ka_UserAuthenticate(pwd->pw_name, "", 0, 1689 pswdp, 1, &reason)) { 1690 (void) sprintf(errbuf, "AFS authentication failed, %s", 1691 reason); 1692 logerr("Attempt by %s; %s", 1693 nbuf, errbuf); 1694 return (errbuf); 1695 } 1696 } 1697#endif 1698 } 1699 if (getuid() != 0) { 1700 if (getuid() == pwd->pw_uid) 1701 return (NULL); 1702 if (strcmp(pwd->pw_name, DEFUSER) == 0) 1703 return (NULL); 1704 logerr("Fileserver not superuser"); 1705 return ("Reason: fileserver is not running privileged"); 1706 } 1707#if CMUCS 1708 tlc.tlc_hostid = TLC_UNKHOST; 1709 tlc.tlc_ttyid = TLC_UNKTTY; 1710 if (okaccess(pwd->pw_name, ACCESS_TYPE_SU, 0, -1, tlc) != 1) 1711 status = ACCESS_CODE_DENIED; 1712 else { 1713 grp = NULL; 1714 acc = NULL; 1715 status = oklogin(pwd->pw_name, group, &account, pswdp, &pwd, &grp, &acc, &grps); 1716 if (status == ACCESS_CODE_OK) { 1717 if ((p = okpassword(pswdp, pwd->pw_name, pwd->pw_gecos)) != NULL) 1718 status = ACCESS_CODE_INSECUREPWD; 1719 } 1720 } 1721#else /* CMUCS */ 1722 status = ACCESS_CODE_OK; 1723 if (namep && strcmp(pwd->pw_name, DEFUSER) != 0) 1724 if (pswdp == NULL || strcmp(pwd->pw_passwd, crypt(pswdp, pwd->pw_passwd))) 1725 status = ACCESS_CODE_BADPASSWORD; 1726#endif /* CMUCS */ 1727 switch (status) { 1728 case ACCESS_CODE_OK: 1729 break; 1730 case ACCESS_CODE_BADPASSWORD: 1731 p = "Reason: Invalid password"; 1732 break; 1733#if CMUCS 1734 case ACCESS_CODE_INSECUREPWD: 1735 (void) sprintf(errbuf, "Reason: %s", p); 1736 p = errbuf; 1737 break; 1738 case ACCESS_CODE_DENIED: 1739 p = "Reason: Access denied"; 1740 break; 1741 case ACCESS_CODE_NOUSER: 1742 p = errbuf; 1743 break; 1744 case ACCESS_CODE_ACCEXPIRED: 1745 p = "Reason: Account expired"; 1746 break; 1747 case ACCESS_CODE_GRPEXPIRED: 1748 p = "Reason: Group expired"; 1749 break; 1750 case ACCESS_CODE_ACCNOTVALID: 1751 p = "Reason: Invalid account"; 1752 break; 1753 case ACCESS_CODE_MANYDEFACC: 1754 p = "Reason: User has more than one default account"; 1755 break; 1756 case ACCESS_CODE_NOACCFORGRP: 1757 p = "Reason: No account for group"; 1758 break; 1759 case ACCESS_CODE_NOGRPFORACC: 1760 p = "Reason: No group for account"; 1761 break; 1762 case ACCESS_CODE_NOGRPDEFACC: 1763 p = "Reason: No group for default account"; 1764 break; 1765 case ACCESS_CODE_NOTGRPMEMB: 1766 p = "Reason: Not member of group"; 1767 break; 1768 case ACCESS_CODE_NOTDEFMEMB: 1769 p = "Reason: Not member of default group"; 1770 break; 1771 case ACCESS_CODE_OOPS: 1772 p = "Reason: Internal error"; 1773 break; 1774#endif /* CMUCS */ 1775 default: 1776 (void) sprintf(p = errbuf, "Reason: Status %d", status); 1777 break; 1778 } 1779 if (status != ACCESS_CODE_OK) { 1780 logerr("Login failure for %s", pwd->pw_name); 1781 logerr("%s", p); 1782#if CMUCS 1783 logaccess(pwd->pw_name, ACCESS_TYPE_SUP, status, 0, -1, tlc); 1784#endif /* CMUCS */ 1785 return (p); 1786 } 1787#if CMUCS 1788 if (setgroups(grps[0], &grps[1]) < 0) 1789 logerr("setgroups: %%m"); 1790 if (setgid((gid_t) grp->gr_gid) < 0) 1791 logerr("setgid: %%m"); 1792 if (setuid((uid_t) pwd->pw_uid) < 0) 1793 logerr("setuid: %%m"); 1794#else /* CMUCS */ 1795 if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) 1796 return ("Error setting group list"); 1797 if (setgid(pwd->pw_gid) < 0) 1798 logerr("setgid: %%m"); 1799 if (setuid(pwd->pw_uid) < 0) 1800 logerr("setuid: %%m"); 1801#endif /* CMUCS */ 1802 return (NULL); 1803} 1804 1805void 1806goaway(const char *fmt, ...) 1807{ 1808 char buf[STRINGLENGTH]; 1809 va_list ap; 1810 1811 va_start(ap, fmt); 1812 (void) netcrypt(NULL); 1813 1814 vsnprintf(buf, sizeof(buf), fmt, ap); 1815 va_end(ap); 1816 goawayreason = estrdup(buf); 1817 (void) msggoaway(); 1818 logerr("%s: %s", remotehost(), buf); 1819 longjmp(sjbuf, TRUE); 1820} 1821 1822char * 1823fmttime(time_t time) 1824{ 1825 static char buf[STRINGLENGTH]; 1826 unsigned int len; 1827 1828 (void) strcpy(buf, ctime(&time)); 1829 len = strlen(buf + 4) - 6; 1830 (void) strncpy(buf, buf + 4, len); 1831 buf[len] = '\0'; 1832 return (buf); 1833} 1834/* 1835 * Determine whether the file referenced by the file descriptor 'handle' can 1836 * be trusted, namely is it a file resident in the local file system. 1837 * 1838 * The main method of operation is to perform operations on the file 1839 * descriptor so that an attempt to spoof the checks should fail, for 1840 * example renamimg the file from underneath us and/or changing where the 1841 * file lives from underneath us. 1842 * 1843 * returns: -1 for error, indicating that we can not tell 1844 * 0 for file is definately not local, or it is an RFS link 1845 * 1 for file is local and can be trusted 1846 * 1847 * Side effect: copies the stat information into the supplied buffer, 1848 * regardless of the type of file system the file resides. 1849 * 1850 * Currently, the cases that we try to distinguish are RFS, AFS, NFS and 1851 * UFS, where the latter is considered a trusted file. We assume that the 1852 * caller has disabled link following and will detect an attempt to access 1853 * a file through an RFS link, except in the case the last component is 1854 * an RFS link. With link following disabled, the last component itself is 1855 * interpreted as a regular file if it is really an RFS link, so we 1856 * disallow the RFS link identified by group "symlink" and mode "IEXEC by 1857 * owner only". An AFS file is 1858 * detected by trying the VIOCIGETCELL ioctl, which is one of the few AFS 1859 * ioctls which operate on a file descriptor. Note, this AFS ioctl is 1860 * implemented in the cache manager, so the decision does not involve a 1861 * query with the AFS file server. An NFS file is detected by looking at 1862 * the major device number and seeing if it matches the known values for 1863 * MACH NSF/Sun OS 3.x or Sun OS 4.x. 1864 * 1865 * Having the fstatvfs() system call would make this routine easier and 1866 * more reliable. 1867 * 1868 * Note, in order to make the checks simpler, the file referenced by the 1869 * file descriptor can not be a BSD style symlink. Even with symlink 1870 * following of the last path component disabled, the attempt to open a 1871 * file which is a symlink will succeed, so we check for the BSD symlink 1872 * file type here. Also, the link following on/off and RFS file types 1873 * are only relevant in a MACH environment. 1874 */ 1875#ifdef AFS 1876#include <sys/viceioctl.h> 1877#endif 1878 1879#define SYMLINK_GRP 64 1880 1881int 1882local_file(int handle, struct stat * sinfo) 1883{ 1884 struct stat sb; 1885#ifdef VIOCIGETCELL 1886 /* 1887 * dummies for the AFS ioctl 1888 */ 1889 struct ViceIoctl vdata; 1890 char cellname[512]; 1891#endif /* VIOCIGETCELL */ 1892 1893 if (fstat(handle, &sb) < 0) 1894 return (-1); 1895 if (sinfo != NULL) 1896 *sinfo = sb; 1897 1898#if CMUCS 1899 /* 1900 * If the following test succeeds, then the file referenced by 1901 * 'handle' is actually an RFS link, so we will not trust it. 1902 * See <sys/inode.h>. 1903 */ 1904 if (sb.st_gid == SYMLINK_GRP 1905 && (sb.st_mode & (S_IFMT | S_IEXEC | (S_IEXEC >> 3) | (S_IEXEC >> 6))) 1906 == (S_IFREG | S_IEXEC)) 1907 return (0); 1908#endif /* CMUCS */ 1909 1910 /* 1911 * Do not trust BSD style symlinks either. 1912 */ 1913 if (S_ISLNK(sb.st_mode)) 1914 return (0); 1915 1916#ifdef VIOCIGETCELL 1917 /* 1918 * This is the VIOCIGETCELL ioctl, which takes an fd, not 1919 * a path name. If it succeeds, then the file is in AFS. 1920 * 1921 * On failure, ENOTTY indicates that the file was not in 1922 * AFS; all other errors are pessimistically assumed to be 1923 * a temporary AFS error. 1924 */ 1925 vdata.in_size = 0; 1926 vdata.out_size = sizeof(cellname); 1927 vdata.out = cellname; 1928 if (ioctl(handle, VIOCIGETCELL, (char *) &vdata) != -1) 1929 return (0); 1930 if (errno != ENOTTY) 1931 return (-1); 1932#endif /* VIOCIGETCELL */ 1933 1934 /* 1935 * Verify the file is not in NFS. 1936 * 1937 * Our current implementation and Sun OS 3.x use major device 1938 * 255 for NFS files; Sun OS 4.x seems to use 130 (I have only 1939 * determined this empirically -- DLC). Without a fstatvfs() 1940 * system call, this will have to do for now. 1941 */ 1942#if defined(__SVR4) || __NetBSD_Version__ > 299000900 1943 { 1944 struct statvfs sf; 1945 1946 if (fstatvfs(handle, &sf) == -1) 1947 return (-1); 1948#ifdef __SVR4 1949 return strncmp(sf.f_basetype, "nfs", 3) != 0; 1950#else 1951 return strncmp(sf.f_fstypename, "nfs", 3) != 0; 1952#endif 1953 } 1954#elif defined(__NetBSD__) 1955 { 1956 struct statfs sf; 1957 if (fstatfs(handle, &sf) == -1) 1958 return (-1); 1959 return strncmp(sf.f_fstypename, "nfs", 3) != 0; 1960 } 1961#else 1962 if (major(sb.st_dev) == 255 || major(sb.st_dev) == 130) 1963 return (0); 1964 else 1965 return (1); 1966#endif 1967 1968} 1969/* 1970 * Companion routine for ensuring that a local file can be trusted. Compare 1971 * various pieces of the stat information to make sure that the file can be 1972 * trusted. Returns true for stat information which meets the criteria 1973 * for being trustworthy. The main paranoia is to prevent a hard link to 1974 * a root owned file. Since the link could be removed after the file is 1975 * opened, a simply fstat() can not be relied upon. The two stat buffers 1976 * for comparison should come from a stat() on the file name and a following 1977 * fstat() on the open file. Some of the following checks are also an 1978 * additional level of paranoia. Also, this test will fail (correctly) if 1979 * either or both of the stat structures have all fields zeroed; typically 1980 * due to a stat() failure. 1981 */ 1982 1983 1984int 1985stat_info_ok(struct stat * sb1, struct stat * sb2) 1986{ 1987 return (sb1->st_ino == sb2->st_ino && /* Still the same file */ 1988 sb1->st_dev == sb2->st_dev && /* On the same device */ 1989 sb1->st_mode == sb2->st_mode && /* Perms (and type) same */ 1990 S_ISREG(sb1->st_mode) && /* Only allow reg files */ 1991 (sb1->st_mode & 077) == 0 && /* Owner only perms */ 1992 sb1->st_nlink == sb2->st_nlink && /* # hard links same... */ 1993 sb1->st_nlink == 1 && /* and only 1 */ 1994 sb1->st_uid == sb2->st_uid && /* owner and ... */ 1995 sb1->st_gid == sb2->st_gid && /* group unchanged */ 1996 sb1->st_mtime == sb2->st_mtime && /* Unmodified between stats */ 1997 sb1->st_ctime == sb2->st_ctime); /* Inode unchanged. Hopefully 1998 * a catch-all paranoid test */ 1999} 2000#if MACH 2001/* 2002 * Twiddle symbolic/RFS link following on/off. This is a no-op in a non 2003 * CMUCS/MACH environment. Also, the setmodes/getmodes interface is used 2004 * mainly because it is simpler than using table(2) directly. 2005 */ 2006#include <sys/table.h> 2007 2008int 2009link_nofollow(int on) 2010{ 2011 static int modes = -1; 2012 2013 if (modes == -1 && (modes = getmodes()) == -1) 2014 return (-1); 2015 if (on) 2016 return (setmodes(modes | UMODE_NOFOLLOW)); 2017 return (setmodes(modes)); 2018} 2019#else /* MACH */ 2020/*ARGSUSED*/ 2021int 2022link_nofollow(int on __unused) 2023{ 2024 return (0); 2025} 2026#endif /* MACH */ 2027