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