1 /* $NetBSD: supfilesrv.c,v 1.42 2009/10/16 12:41:37 christos Exp $ */
4 * Copyright (c) 1992 Carnegie Mellon University
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.
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.
17 * Carnegie Mellon requests users of this software to return to
19 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
20 * School of Computer Science
21 * Carnegie Mellon University
22 * Pittsburgh PA 15213-3890
24 * any improvements or extensions that they make and grant Carnegie Mellon
25 * the rights to redistribute these changes.
29 * supfilesrv -- SUP File Server
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
39 **********************************************************************
41 * 2-Aug-99 Manuel Bouyer at LIP6
42 * Added libwrap support
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.
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
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.
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.
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
72 * [90/12/25 15:15:15 ern]
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
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
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
111 * Revision 1.14 89/08/23 14:56:15 gm0w
112 * Changed msgf routines to msg routines.
115 * Revision 1.13 89/08/03 19:57:33 mja
116 * Remove setaid() call.
118 * Revision 1.12 89/08/03 19:49:24 mja
119 * Updated to use v*printf() in place of _doprnt().
122 * 11-Sep-88 Glenn Marcy (gm0w) at Carnegie-Mellon University
123 * Added code to record release name in logfile.
125 * 18-Mar-88 Glenn Marcy (gm0w) at Carnegie-Mellon University
126 * Added host=<hostfile> support to releases file. [V7.12]
128 * 27-Dec-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
129 * Added crosspatch support. Created docrypt() routine for crypt
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.
136 * 28-Jun-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
137 * Added code for "release" support. [V5.11]
139 * 26-May-87 Doug Philips (dwp) at Carnegie-Mellon University
140 * Added code to record final status of client in logfile. [V5.10]
142 * 22-May-87 Chriss Stephens (chriss) at Carnegie Mellon University
143 * Mergered divergent CS and ECE versions. [V5.9a]
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]
149 * 31-Mar-87 Dan Nydick (dan) at Carnegie-Mellon University
150 * Fixed so no password check is done when crypts are used.
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]
157 * 20-Oct-86 Dan Nydick (dan) at Carnegie-Mellon University
158 * Changed not to call okmumbles when not compiled with CMUCS.
160 * 04-Aug-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
161 * Added code to increment scmdebug as more -N flags are
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]
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
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]
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]
184 * 29-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
185 * Major rewrite for protocol version 4. [V4.2]
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.
191 * 24-Nov-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
192 * Allow "!hostname" lines and comments in collection "host" file.
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
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.
202 * 22-Sep-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
203 * Merged 4.1 and 4.2 versions together.
205 * 04-Jun-85 Steven Shafer (sas) at Carnegie-Mellon University
206 * Created for 4.2 BSD.
208 **********************************************************************
212 #include <afs/param.h>
215 #include <sys/param.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
236 #include <sys/ioctl.h>
240 #include <sys/ttyloc.h>
242 #include <sys/viceioctl.h>
244 #define ACCESS_CODE_OK 0
245 #define ACCESS_CODE_BADPASSWORD (-2)
249 #include <sys/mkdev.h>
250 #include <sys/statvfs.h>
256 #include "supcdefs.h"
257 #include "supextern.h"
263 extern char *crypt(const char *, const char *);
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.
275 #define PGMVERSION 13
277 /*************************
279 *************************/
282 #define HASHSIZE (1<<HASHBITS)
283 #define HASHMASK (HASHSIZE-1)
284 #define HASHFUNC(x,y) ((x)&HASHMASK)
286 /*******************************************
287 *** D A T A S T R U C T U R E S ***
288 *******************************************/
290 struct hashstruct
{ /* hash table for number lists */
291 int Hnum1
; /* numeric keys */
293 char *Hname
; /* string value */
294 TREE
*Htree
; /* TREE value */
295 struct hashstruct
*Hnext
;
297 typedef struct hashstruct HASH
;
299 /*********************************************
300 *** G L O B A L V A R I A B L E S ***
301 *********************************************/
303 char program
[] = "supfilesrv"; /* program name for SCM messages */
304 int progpid
= -1; /* and process id */
306 jmp_buf sjbuf
; /* jump location for network errors */
307 TREELIST
*listTL
; /* list of trees to upgrade */
309 int silent
; /* -S flag */
311 int sup_clog
; /* -l flag */
313 int live
; /* -d flag */
314 int dbgportsq
; /* -P flag */
315 extern int scmdebug
; /* -N flag */
318 int candorcs
; /* -R flag */
322 int nchildren
; /* number of children that exist */
323 char *prefix
; /* collection pathname prefix */
324 char *release
; /* collection release name */
325 char *cryptkey
; /* encryption key if non-null */
327 char *cvs_root
; /* RCS root */
329 char *rcs_branch
; /* RCS branch name */
330 int lockfd
; /* descriptor of lock file */
332 /* global variables for scan functions */
333 int trace
= FALSE
; /* directory scan trace */
334 int cancompress
= FALSE
; /* Can we compress files */
335 int docompress
= FALSE
; /* Do we compress files */
337 HASH
*uidH
[HASHSIZE
]; /* for uid and gid lookup */
338 HASH
*gidH
[HASHSIZE
];
339 HASH
*inodeH
[HASHSIZE
]; /* for inode lookup for linked file check */
343 int main(int, char **);
346 void init(int, char **);
348 void srvsignon(void);
352 void listfiles(void);
353 int denyone(TREE
*, void *);
354 void send_files(void);
355 int send_one(TREE
*, void *);
356 int send_dir(TREE
*, void *);
357 int send_file(TREE
*, va_list);
358 void srvfinishup(time_t);
360 HASH
*Hlookup(HASH
**, int, int);
361 void Hinsert(HASH
**, int, int, char *, TREE
*);
362 TREE
*linkcheck(TREE
*, int, int);
365 char *changeuid(char *, char *, int, int);
366 void goaway(const char *, ...);
367 char *fmttime(time_t);
368 int local_file(int, struct stat
*);
369 int stat_info_ok(struct stat
*, struct stat
*);
370 int link_nofollow(int);
371 int link_nofollow(int);
373 /*************************************
374 *** M A I N R O U T I N E ***
375 *************************************/
378 main(int argc
, char **argv
)
382 struct sigaction chld
, ign
;
385 struct request_info req
;
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 */
394 init(argc
, argv
); /* process arguments */
397 if (!live
) /* if not debugging, turn into daemon */
402 tloc
= time((time_t *) NULL
);
403 loginfo("SUP File Server Version %d.%d (%s) starting at %s",
404 PROTOVERSION
, PGMVERSION
, scmversion
, fmttime(tloc
));
409 logquit(1, "Can't connect to network");
411 request_init(&req
, RQ_DAEMON
, "supfilesrv", RQ_FILE
, netfile
,
414 if (hosts_access(&req
) == 0) {
415 logdeny("refused connection from %.500s",
421 logallow("connection from %.500s", eval_client(&req
));
428 ign
.sa_handler
= SIG_IGN
;
429 sigemptyset(&ign
.sa_mask
);
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
);
437 (void) sigaction(SIGCHLD
, &chld
, NULL
);
442 logerr("Error in establishing network connection");
443 (void) servicekill();
447 sigaddset(&nset
, SIGCHLD
);
448 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
449 if ((pid
= fork()) == 0) { /* server process */
451 request_init(&req
, RQ_DAEMON
, "supfilesrv", RQ_FILE
,
454 if (hosts_access(&req
) == 0) {
455 logdeny("refused connection from %.500s",
461 logallow("connection from %.500s",
465 (void) serviceprep();
470 (void) servicekill(); /* parent */
473 (void) sigprocmask(SIG_SETMASK
, &oset
, NULL
);
477 * Child status signal handler
481 chldsig(int snum __unused
)
485 while (wait3((int *) &w
, WNOHANG
, (struct rusage
*) 0) > 0) {
490 /*****************************************
491 *** I N I T I A L I Z A T I O N ***
492 *****************************************/
498 quit(1, "Usage: supfilesrv [ -4 | -6 | -l | -d | -P | -N | -C <max children> | -H <host> <user> <cryptfile> <supargs> ]\n");
500 quit(1, "Usage: supfilesrv [ -4 | -6 | -d | -P | -N | -C <max children> | -H <host> <user> <cryptfile> <supargs> ]\n");
505 init(int argc
, char **argv
)
509 char *clienthost
, *clientuser
;
511 char buf
[STRINGLENGTH
];
531 while (clienthost
== NULL
&& argc
> 0 && argv
[0][0] == '-') {
532 switch (argv
[0][1]) {
552 quit(1, "Missing arg to -C\n");
554 maxchildren
= atoi(argv
[0]);
558 quit(1, "Missing args to -H\n");
560 clienthost
= argv
[0];
561 clientuser
= argv
[1];
580 fprintf(stderr
, "Unknown flag %s ignored\n", argv
[0]);
586 if (clienthost
== NULL
) {
589 x
= servicesetup(dbgportsq
? DEBUGFPORT
: FILEPORT
, af
);
591 quit(1, "Error in network setup");
592 for (i
= 0; i
< HASHSIZE
; i
++)
593 uidH
[i
] = gidH
[i
] = inodeH
[i
] = NULL
;
599 f
= fopen(cryptkey
, "r");
601 quit(1, "Unable to open cryptfile %s\n", cryptkey
);
602 if ((p
= fgets(buf
, STRINGLENGTH
, f
)) != NULL
) {
603 if ((q
= strchr(p
, '\n')) != NULL
)
606 quit(1, "No cryptkey found in %s\n", cryptkey
);
607 cryptkey
= estrdup(buf
);
610 x
= request(dbgportsq
? DEBUGFPORT
: FILEPORT
, clienthost
, &maxsleep
);
612 quit(1, "Unable to connect to host %s\n", clienthost
);
615 quit(1, "Error sending signon request to fileserver\n");
618 quit(1, "Error reading signon reply from fileserver\n");
619 printf("SUP Fileserver %d.%d (%s) %d on %s\n",
620 protver
, pgmver
, scmver
, fspid
, remotehost());
624 quit(1, "Remote fileserver does not implement reverse sup\n");
629 quit(1, "Error sending setup request to fileserver\n");
632 quit(1, "Error reading setup reply from fileserver\n");
637 quit(1, "User %s not found on remote client\n", xuser
);
639 quit(1, "This host has no permission to reverse sup\n");
641 quit(1, "Unrecognized file server setup status %d\n", setupack
);
643 if (netcrypt(cryptkey
) != SCMOK
)
644 quit(1, "Running non-crypting fileserver\n");
645 crypttest
= CRYPTTEST
;
648 quit(1, "Error sending encryption test request\n");
651 quit(1, "Data encryption test failed\n");
653 quit(1, "Error reading encryption test reply\n");
654 logcrypt
= CRYPTTEST
;
657 if (netcrypt(PSWDCRYPT
) != SCMOK
) /* encrypt password data */
658 quit(1, "Running non-crypting fileserver\n");
660 (void) netcrypt((char *) NULL
); /* turn off encryption */
662 quit(1, "Error sending login request to file server\n");
665 quit(1, "Error reading login reply from file server\n");
666 if (logack
== FLOGNG
)
667 quit(1, "%s\nImproper login to %s account\n", logerror
, xuser
);
672 quit(1, "Error sending crosspatch request\n");
676 /*****************************************
677 *** A N S W E R R E Q U E S T ***
678 *****************************************/
686 progpid
= fspid
= getpid();
698 starttime
= time((time_t *) NULL
);
699 if (!setjmp(sjbuf
)) {
712 xargv
[xargc
] = (char *) NULL
;
713 (void) dup2(netfile
, 0);
714 (void) dup2(netfile
, 1);
715 (void) dup2(netfile
, 2);
716 fd
= getdtablesize();
719 execvp(xargv
[0], xargv
);
725 srvfinishup(starttime
);
741 if (donereason
== goawayreason
)
748 (void) close(lockfd
);
758 /*****************************************
759 *** S I G N O N C L I E N T ***
760 *****************************************/
770 goaway("Error reading signon request from client");
773 goaway("Error sending signon reply to client");
777 /*****************************************************************
778 *** E X C H A N G E S E T U P I N F O R M A T I O N ***
779 *****************************************************************/
786 char buf
[STRINGLENGTH
];
796 goaway("Error reading setup request from client");
798 setupack
= FSETUPOLD
;
799 (void) msgsetupack();
801 longjmp(sjbuf
, TRUE
);
802 goaway("Sup client using obsolete version of protocol");
807 if ((pw
= getpwnam(xuser
)) == NULL
) {
808 setupack
= FSETUPSAME
;
809 (void) msgsetupack();
811 longjmp(sjbuf
, TRUE
);
812 goaway("User `%s' not found", xuser
);
815 xuser
= estrdup(pw
->pw_dir
);
817 /* check crosspatch host access file */
819 (void) sprintf(buf
, FILEXPATCH
, xuser
);
821 /* Turn off link following */
822 if (link_nofollow(1) != -1) {
824 /* get stat info before open */
825 if (stat(buf
, &sbuf
) == -1)
826 (void) bzero(&sbuf
, sizeof(sbuf
));
828 if ((f
= fopen(buf
, "r")) != NULL
) {
831 while ((p
= fgets(buf
, STRINGLENGTH
, f
)) != NULL
) {
835 if (strchr("#;:", *p
))
837 q
= nxtarg(&p
, " \t");
843 cryptkey
= estrdup(p
);
845 if (local_file(fileno(f
), &fsbuf
) > 0
846 && stat_info_ok(&sbuf
, &fsbuf
)) {
847 runas_uid
= sbuf
.st_uid
;
848 runas_gid
= sbuf
.st_gid
;
854 /* Restore link following */
855 if (link_nofollow(0) == -1)
856 goaway("Restore link following");
859 setupack
= FSETUPHOST
;
860 (void) msgsetupack();
862 longjmp(sjbuf
, TRUE
);
863 goaway("Host not on access list");
869 goaway("Error sending setup reply to client");
873 if (candorcs
&& release
!= NULL
&&
874 (strncmp(release
, "RCS.", 4) == 0)) {
875 rcs_branch
= estrdup(&release
[4]);
877 release
= estrdup("RCS");
882 release
= estrdup(DEFRELEASE
);
883 if (basedir
== NULL
|| *basedir
== '\0') {
885 (void) sprintf(buf
, FILEDIRS
, DEFDIR
);
888 while ((p
= fgets(buf
, STRINGLENGTH
, f
)) != NULL
) {
892 if (strchr("#;:", *p
))
894 q
= nxtarg(&p
, " \t=");
895 if (strcmp(q
, collname
) == 0) {
896 basedir
= skipover(p
, " \t=");
897 basedir
= estrdup(basedir
);
903 if (basedir
== NULL
) {
904 (void) sprintf(buf
, FILEBASEDEFAULT
, collname
);
905 basedir
= estrdup(buf
);
908 if (chdir(basedir
) < 0)
909 goaway("Can't chdir to base directory %s", basedir
);
910 (void) sprintf(buf
, FILEPREFIX
, collname
);
913 while ((p
= fgets(buf
, STRINGLENGTH
, f
)) != NULL
) {
917 if (strchr("#;:", *p
))
920 if (chdir(prefix
) < 0)
921 goaway("Can't chdir to %s from base directory %s",
927 x
= stat(".", &sbuf
);
929 (void) chdir(basedir
);
931 goaway("Can't stat base/prefix directory");
932 if (nchildren
>= maxchildren
) {
933 setupack
= FSETUPBUSY
;
934 (void) msgsetupack();
936 longjmp(sjbuf
, TRUE
);
937 goaway("Sup client told to try again later");
939 if (sbuf
.st_dev
== basedev
&& sbuf
.st_ino
== baseino
&& samehost()) {
940 setupack
= FSETUPSAME
;
941 (void) msgsetupack();
943 longjmp(sjbuf
, TRUE
);
944 goaway("Attempt to upgrade to same directory on same host");
946 /* obtain release information */
947 if (!getrelease(release
)) {
948 setupack
= FSETUPRELEASE
;
949 (void) msgsetupack();
951 longjmp(sjbuf
, TRUE
);
952 goaway("Invalid release information");
954 /* check host access file */
956 for (tl
= listTL
; tl
!= NULL
; tl
= tl
->TLnext
) {
958 if ((h
= tl
->TLhost
) == NULL
)
960 (void) sprintf(buf
, FILEHOST
, collname
, h
);
964 while ((p
= fgets(buf
, STRINGLENGTH
, f
)) != NULL
) {
969 if (strchr("#;:", *p
))
971 q
= nxtarg(&p
, " \t");
972 if ((not = (*q
== '!')) && *++q
== '\0')
973 q
= nxtarg(&p
, " \t");
974 hostok
= (not == (matchhost(q
) == 0));
976 while ((*p
== ' ') || (*p
== '\t'))
979 cryptkey
= estrdup(p
);
985 setupack
= FSETUPHOST
;
986 (void) msgsetupack();
988 longjmp(sjbuf
, TRUE
);
989 goaway("Host not on access list for %s",
994 /* try to lock collection */
995 (void) sprintf(buf
, FILELOCK
, collname
);
997 x
= open(buf
, O_RDONLY
, 0);
999 if (flock(x
, (LOCK_SH
| LOCK_NB
)) < 0) {
1001 if (errno
!= EWOULDBLOCK
)
1002 goaway("Can't lock collection %s", collname
);
1003 setupack
= FSETUPBUSY
;
1004 (void) msgsetupack();
1006 longjmp(sjbuf
, TRUE
);
1007 goaway("Sup client told to wait for lock");
1012 setupack
= FSETUPOK
;
1015 goaway("Error sending setup reply to client");
1019 /** Test data encryption **/
1024 char buf
[STRINGLENGTH
];
1029 (void) sprintf(buf
, FILECRYPT
, collname
);
1031 /* Turn off link following */
1032 if (link_nofollow(1) != -1) {
1033 /* get stat info before open */
1034 if (stat(buf
, &sbuf
) == -1)
1035 (void) bzero(&sbuf
, sizeof(sbuf
));
1037 if ((f
= fopen(buf
, "r")) != NULL
) {
1040 if (cryptkey
== NULL
&&
1041 (p
= fgets(buf
, STRINGLENGTH
, f
))) {
1042 if ((q
= strchr(p
, '\n')) != NULL
)
1045 cryptkey
= estrdup(buf
);
1047 if (local_file(fileno(f
), &fsbuf
) > 0
1048 && stat_info_ok(&sbuf
, &fsbuf
)) {
1049 runas_uid
= sbuf
.st_uid
;
1050 runas_gid
= sbuf
.st_gid
;
1054 /* Restore link following */
1055 if (link_nofollow(0) == -1)
1056 goaway("Restore link following");
1059 if (netcrypt(cryptkey
) != SCMOK
)
1060 goaway("Runing non-crypting supfilesrv");
1063 goaway("Error reading encryption test request from client");
1064 (void) netcrypt((char *) NULL
);
1065 if (strcmp(crypttest
, CRYPTTEST
) != 0)
1066 goaway("Client not encrypting data properly");
1071 goaway("Error sending encryption test reply to client");
1073 /***************************************************************
1074 *** C O N N E C T T O P R O P E R A C C O U N T ***
1075 ***************************************************************/
1080 int x
, fileuid
= -1, filegid
= -1;
1082 (void) netcrypt(PSWDCRYPT
); /* encrypt acct name and password */
1084 (void) netcrypt((char *) NULL
); /* turn off encryption */
1086 goaway("Error reading login request from client");
1088 if (strcmp(logcrypt
, CRYPTTEST
) != 0) {
1090 logerror
= "Improper login encryption";
1092 goaway("Client not encrypting login information properly");
1097 if (loguser
== NULL
) {
1099 if (runas_uid
>= 0 && runas_gid
>= 0) {
1100 fileuid
= runas_uid
;
1101 filegid
= runas_gid
;
1104 loguser
= estrdup(DEFUSER
);
1106 loguser
= estrdup(DEFUSER
);
1108 if ((logerror
= changeuid(loguser
, logpswd
, fileuid
, filegid
)) != NULL
) {
1112 longjmp(sjbuf
, TRUE
);
1113 goaway("Client denied login access");
1122 goaway("Error sending login reply to client");
1123 if (!xpatch
) /* restore desired encryption */
1124 if (netcrypt(cryptkey
) != SCMOK
)
1125 goaway("Running non-crypting supfilesrv");
1129 /*****************************************
1130 *** M A K E N A M E L I S T ***
1131 *****************************************/
1141 goaway("Error reading refuse list from client");
1146 goaway("Error sending file list to client");
1152 goaway("Error reading needed files list from client");
1154 (void) Tprocess(needT
, denyone
, NULL
);
1158 goaway("Error sending denied files list to client");
1164 denyone(TREE
* t
, void *v __unused
)
1167 char *name
= t
->Tname
;
1168 int update
= (t
->Tflags
& FUPDATE
) != 0;
1171 char slinkname
[STRINGLENGTH
];
1174 for (tl
= listTL
; tl
!= NULL
; tl
= tl
->TLnext
)
1175 if ((t
= Tsearch(tl
->TLtree
, name
)) != NULL
)
1178 (void) Tinsert(&denyT
, name
, FALSE
);
1181 cdprefix(tl
->TLprefix
);
1182 if (S_ISLNK(t
->Tmode
))
1183 x
= lstat(name
, &sbuf
);
1185 x
= stat(name
, &sbuf
);
1186 if (x
< 0 || (sbuf
.st_mode
& S_IFMT
) != (t
->Tmode
& S_IFMT
)) {
1187 (void) Tinsert(&denyT
, name
, FALSE
);
1190 switch (t
->Tmode
& S_IFMT
) {
1192 if ((x
= readlink(name
, slinkname
, STRINGLENGTH
- 1)) <= 0) {
1193 (void) Tinsert(&denyT
, name
, FALSE
);
1196 slinkname
[x
] = '\0';
1197 (void) Tinsert(&t
->Tlink
, slinkname
, FALSE
);
1200 if (sbuf
.st_nlink
> 1 &&
1201 (tlink
= linkcheck(t
, (int) sbuf
.st_dev
, (int) sbuf
.st_ino
))) {
1202 (void) Tinsert(&tlink
->Tlink
, name
, FALSE
);
1206 t
->Tflags
|= FUPDATE
;
1208 t
->Tuid
= sbuf
.st_uid
;
1209 t
->Tgid
= sbuf
.st_gid
;
1212 (void) Tinsert(&denyT
, name
, FALSE
);
1215 t
->Tflags
|= FNEEDED
;
1218 /*********************************
1219 *** S E N D F I L E S ***
1220 *********************************/
1228 /* Does the protocol support compression */
1230 /* Check for compression on sending files */
1233 goaway("Error sending compression check to server");
1235 /* send all files */
1236 for (tl
= listTL
; tl
!= NULL
; tl
= tl
->TLnext
) {
1237 cdprefix(tl
->TLprefix
);
1240 cvs_root
= getcwd(NULL
, 256);
1241 if (access("CVSROOT", F_OK
) < 0)
1244 loginfo("is a CVSROOT \"%s\"\n", cvs_root
);
1249 (void) Tprocess(tl
->TLtree
, send_one
, NULL
);
1251 /* send directories in reverse order */
1252 for (tl
= listTL
; tl
!= NULL
; tl
= tl
->TLnext
) {
1253 cdprefix(tl
->TLprefix
);
1254 (void) Trprocess(tl
->TLtree
, send_dir
, NULL
);
1258 goaway("Error reading receive file request from client");
1260 x
= msgrecv(send_file
, 0);
1262 goaway("Error sending file to client");
1266 send_one(TREE
* t
, void *v __unused
)
1269 char temp_file
[STRINGLENGTH
];
1270 char *av
[50]; /* More than enough */
1272 if ((t
->Tflags
& FNEEDED
) == 0) /* only send needed files */
1274 if (S_ISDIR(t
->Tmode
)) /* send no directories this pass */
1278 goaway("Error reading receive file request from client");
1279 upgradeT
= t
; /* upgrade file pointer */
1280 fd
= -1; /* no open file */
1281 if (S_ISREG(t
->Tmode
)) {
1282 if (!listonly
&& (t
->Tflags
& FUPDATE
) == 0) {
1285 char rcs_release
[STRINGLENGTH
];
1288 fd
= open(rcs_file
, (O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
), 0600);
1290 goaway("We died trying to create temp file");
1293 if (strcmp(&t
->Tname
[strlen(t
->Tname
) - 2], ",v") == 0) {
1294 t
->Tname
[strlen(t
->Tname
) - 2] = '\0';
1299 av
[ac
++] = cvs_root
;
1305 if (rcs_branch
!= NULL
) {
1307 av
[ac
++] = rcs_branch
;
1313 if (rcs_branch
!= NULL
) {
1314 sprintf(rcs_release
, "-r%s",
1316 av
[ac
++] = rcs_release
;
1319 av
[ac
++] = t
->Tname
;
1321 status
= runio(av
, NULL
, rcs_file
,
1323 /* loginfo("using rcs mode \n"); */
1324 if (status
< 0 || WEXITSTATUS(status
)) {
1328 goaway("We died trying to run cvs or rcs on %s", rcs_file
);
1332 logerr("rcs command failed = %d\n",
1333 WEXITSTATUS(status
));
1335 t
->Tflags
|= FUPDATE
;
1337 } else if (docompress
) {
1342 if (runio(av
, rcs_file
, temp_file
, NULL
) != 0) {
1346 goaway("We died trying to gzip %s", rcs_file
);
1349 fd
= open(temp_file
, O_RDONLY
, 0);
1351 fd
= open(rcs_file
, O_RDONLY
, 0);
1357 snprintf(temp_file
, sizeof(temp_file
),
1358 "%s/supfilesrv.XXXXXX", P_tmpdir
);
1359 fd
= mkstemp(temp_file
);
1361 goaway("We died trying to create temp file");
1367 if (runio(av
, t
->Tname
, temp_file
, NULL
) != 0) {
1370 goaway("We died trying to gzip %s", t
->Tname
);
1373 fd
= open(temp_file
, O_RDONLY
, 0);
1375 fd
= open(t
->Tname
, O_RDONLY
, 0);
1377 if (fd
< 0 && (t
->Tflags
& FUPDATE
) == 0)
1381 t
->Tuser
= estrdup(uconvert(t
->Tuid
));
1382 t
->Tgroup
= estrdup(gconvert(t
->Tgid
));
1385 x
= msgrecv(send_file
, fd
);
1393 goaway("Error sending file %s to client", t
->Tname
);
1398 send_dir(TREE
* t
, void *v __unused
)
1402 if ((t
->Tflags
& FNEEDED
) == 0) /* only send needed files */
1404 if (!S_ISDIR(t
->Tmode
)) /* send only directories this pass */
1408 goaway("Error reading receive file request from client");
1409 upgradeT
= t
; /* upgrade file pointer */
1410 t
->Tuser
= estrdup(uconvert(t
->Tuid
));
1411 t
->Tgroup
= estrdup(gconvert(t
->Tgid
));
1412 x
= msgrecv(send_file
, 0);
1414 goaway("Error sending file %s to client", t
->Tname
);
1419 send_file(TREE
* t
, va_list ap
)
1423 fd
= va_arg(ap
, int);
1424 if (!S_ISREG(t
->Tmode
) || listonly
|| (t
->Tflags
& FUPDATE
))
1428 goaway("Error sending file %s to client", t
->Tname
);
1432 /*****************************************
1433 *** E N D C O N N E C T I O N ***
1434 *****************************************/
1437 srvfinishup(time_t starttime
)
1440 char tmpbuf
[BUFSIZ
], *p
, lognam
[STRINGLENGTH
];
1445 (void) netcrypt((char *) NULL
);
1447 if (goawayreason
!= NULL
)
1449 goawayreason
= (char *) NULL
;
1451 doneack
= FDONESUCCESS
;
1452 donereason
= estrdup("Unknown");
1453 } else if (goawayreason
== (char *) NULL
)
1456 doneack
= FDONEGOAWAY
;
1457 donereason
= goawayreason
;
1459 if (x
== SCMEOF
|| x
== SCMERR
) {
1460 doneack
= FDONEUSRERROR
;
1461 donereason
= estrdup("Premature EOF on network");
1462 } else if (x
!= SCMOK
) {
1463 doneack
= FDONESRVERROR
;
1464 donereason
= estrdup("Unknown SCM code");
1466 if (doneack
== FDONEDONTLOG
)
1468 if (donereason
== NULL
)
1469 donereason
= estrdup("No reason");
1470 if (doneack
== FDONESRVERROR
|| doneack
== FDONEUSRERROR
)
1471 logerr("%s", donereason
);
1472 else if (doneack
== FDONEGOAWAY
)
1473 logerr("GOAWAY: %s", donereason
);
1474 else if (doneack
!= FDONESUCCESS
)
1475 logerr("Reason %d: %s", doneack
, donereason
);
1476 goawayreason
= donereason
;
1477 cdprefix((char *) NULL
);
1478 if (collname
== NULL
) {
1479 logerr("NULL collection in svrfinishup");
1482 (void) sprintf(lognam
, FILELOGFILE
, collname
);
1483 if ((logfd
= open(lognam
, O_APPEND
| O_WRONLY
, 0644)) < 0)
1484 return; /* can not open file up...error */
1485 finishtime
= time((time_t *) NULL
);
1487 (void) sprintf(p
, "%s ", fmttime(lasttime
));
1489 (void) sprintf(p
, "%s ", fmttime(starttime
));
1491 (void) sprintf(p
, "%s ", fmttime(finishtime
));
1493 if ((releasename
= release
) == NULL
)
1494 releasename
= "UNKNOWN";
1495 (void) sprintf(p
, "%s %s %d %s\n", remotehost(), releasename
,
1496 FDONESUCCESS
- doneack
, donereason
);
1499 /* if we are busy dont get stuck updating the disk if full */
1500 if (setupack
== FSETUPBUSY
) {
1501 long l
= FIOCNOSPC_ERROR
;
1502 ioctl(logfd
, FIOCNOSPC
, &l
);
1505 (void) write(logfd
, tmpbuf
, (p
- tmpbuf
));
1506 (void) close(logfd
);
1508 /***************************************************
1509 *** H A S H T A B L E R O U T I N E S ***
1510 ***************************************************/
1513 Hfree(HASH
** table
)
1517 for (i
= 0; i
< HASHSIZE
; i
++)
1518 while ((h
= table
[i
]) != NULL
) {
1519 table
[i
] = h
->Hnext
;
1527 Hlookup(HASH
** table
, int num1
, int num2
)
1531 hno
= HASHFUNC(num1
, num2
);
1532 for (h
= table
[hno
]; h
&& (h
->Hnum1
!= num1
|| h
->Hnum2
!= num2
); h
= h
->Hnext
);
1537 Hinsert(HASH
** table
, int num1
, int num2
, char *name
, TREE
* tree
)
1541 hno
= HASHFUNC(num1
, num2
);
1542 h
= (HASH
*) malloc(sizeof(HASH
));
1544 goaway("Cannot allocate memory");
1549 h
->Hnext
= table
[hno
];
1552 /*********************************************
1553 *** U T I L I T Y R O U T I N E S ***
1554 *********************************************/
1557 linkcheck(TREE
* t
, int d
, int i
)
1558 /* inode # and device # */
1561 h
= Hlookup(inodeH
, i
, d
);
1564 Hinsert(inodeH
, i
, d
, (char *) NULL
, t
);
1565 return ((TREE
*) NULL
);
1574 u
= Hlookup(uidH
, uid
, 0);
1580 p
= estrdup(pw
->pw_name
);
1581 Hinsert(uidH
, uid
, 0, p
, (TREE
*) NULL
);
1591 g
= Hlookup(gidH
, gid
, 0);
1597 p
= estrdup(gr
->gr_name
);
1598 Hinsert(gidH
, gid
, 0, p
, (TREE
*) NULL
);
1603 changeuid(char *namep
, char *passwordp
, int fileuid
, int filegid
)
1605 char *group
, *account
, *pswdp
;
1609 struct account
*acc
;
1612 int status
= ACCESS_CODE_OK
;
1613 char nbuf
[STRINGLENGTH
];
1614 static char errbuf
[STRINGLENGTH
];
1620 if (namep
== NULL
) {
1621 pwd
= getpwuid(fileuid
);
1623 (void) sprintf(errbuf
, "Reason: Unknown user id %d",
1627 grp
= getgrgid(filegid
);
1629 group
= strcpy(nbuf
, grp
->gr_name
);
1635 (void) strcpy(nbuf
, namep
);
1636 account
= group
= strchr(nbuf
, ',');
1637 if (group
!= NULL
) {
1639 account
= strchr(group
, ',');
1640 if (account
!= NULL
) {
1642 if (*account
== '\0')
1648 pwd
= getpwnam(nbuf
);
1650 (void) sprintf(errbuf
, "Reason: Unknown user %s",
1654 if (strcmp(nbuf
, DEFUSER
) == 0)
1657 pswdp
= passwordp
? passwordp
: "";
1659 if (strcmp(nbuf
, DEFUSER
) != 0) {
1661 setpag(); /* set a pag */
1662 if (ka_UserAuthenticate(pwd
->pw_name
, "", 0,
1663 pswdp
, 1, &reason
)) {
1664 (void) sprintf(errbuf
, "AFS authentication failed, %s",
1666 logerr("Attempt by %s; %s",
1673 if (getuid() != 0) {
1674 if (getuid() == pwd
->pw_uid
)
1676 if (strcmp(pwd
->pw_name
, DEFUSER
) == 0)
1678 logerr("Fileserver not superuser");
1679 return ("Reason: fileserver is not running privileged");
1682 tlc
.tlc_hostid
= TLC_UNKHOST
;
1683 tlc
.tlc_ttyid
= TLC_UNKTTY
;
1684 if (okaccess(pwd
->pw_name
, ACCESS_TYPE_SU
, 0, -1, tlc
) != 1)
1685 status
= ACCESS_CODE_DENIED
;
1689 status
= oklogin(pwd
->pw_name
, group
, &account
, pswdp
, &pwd
, &grp
, &acc
, &grps
);
1690 if (status
== ACCESS_CODE_OK
) {
1691 if ((p
= okpassword(pswdp
, pwd
->pw_name
, pwd
->pw_gecos
)) != NULL
)
1692 status
= ACCESS_CODE_INSECUREPWD
;
1696 status
= ACCESS_CODE_OK
;
1697 if (namep
&& strcmp(pwd
->pw_name
, DEFUSER
) != 0)
1698 if (pswdp
== NULL
|| strcmp(pwd
->pw_passwd
, crypt(pswdp
, pwd
->pw_passwd
)))
1699 status
= ACCESS_CODE_BADPASSWORD
;
1702 case ACCESS_CODE_OK
:
1704 case ACCESS_CODE_BADPASSWORD
:
1705 p
= "Reason: Invalid password";
1708 case ACCESS_CODE_INSECUREPWD
:
1709 (void) sprintf(errbuf
, "Reason: %s", p
);
1712 case ACCESS_CODE_DENIED
:
1713 p
= "Reason: Access denied";
1715 case ACCESS_CODE_NOUSER
:
1718 case ACCESS_CODE_ACCEXPIRED
:
1719 p
= "Reason: Account expired";
1721 case ACCESS_CODE_GRPEXPIRED
:
1722 p
= "Reason: Group expired";
1724 case ACCESS_CODE_ACCNOTVALID
:
1725 p
= "Reason: Invalid account";
1727 case ACCESS_CODE_MANYDEFACC
:
1728 p
= "Reason: User has more than one default account";
1730 case ACCESS_CODE_NOACCFORGRP
:
1731 p
= "Reason: No account for group";
1733 case ACCESS_CODE_NOGRPFORACC
:
1734 p
= "Reason: No group for account";
1736 case ACCESS_CODE_NOGRPDEFACC
:
1737 p
= "Reason: No group for default account";
1739 case ACCESS_CODE_NOTGRPMEMB
:
1740 p
= "Reason: Not member of group";
1742 case ACCESS_CODE_NOTDEFMEMB
:
1743 p
= "Reason: Not member of default group";
1745 case ACCESS_CODE_OOPS
:
1746 p
= "Reason: Internal error";
1750 (void) sprintf(p
= errbuf
, "Reason: Status %d", status
);
1753 if (status
!= ACCESS_CODE_OK
) {
1754 logerr("Login failure for %s", pwd
->pw_name
);
1757 logaccess(pwd
->pw_name
, ACCESS_TYPE_SUP
, status
, 0, -1, tlc
);
1762 if (setgroups(grps
[0], &grps
[1]) < 0)
1763 logerr("setgroups: %%m");
1764 if (setgid((gid_t
) grp
->gr_gid
) < 0)
1765 logerr("setgid: %%m");
1766 if (setuid((uid_t
) pwd
->pw_uid
) < 0)
1767 logerr("setuid: %%m");
1769 if (initgroups(pwd
->pw_name
, pwd
->pw_gid
) < 0)
1770 return ("Error setting group list");
1771 if (setgid(pwd
->pw_gid
) < 0)
1772 logerr("setgid: %%m");
1773 if (setuid(pwd
->pw_uid
) < 0)
1774 logerr("setuid: %%m");
1780 goaway(const char *fmt
, ...)
1782 char buf
[STRINGLENGTH
];
1786 (void) netcrypt((char *) NULL
);
1788 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1790 goawayreason
= estrdup(buf
);
1793 longjmp(sjbuf
, TRUE
);
1797 fmttime(time_t time
)
1799 static char buf
[STRINGLENGTH
];
1802 (void) strcpy(buf
, ctime(&time
));
1803 len
= strlen(buf
+ 4) - 6;
1804 (void) strncpy(buf
, buf
+ 4, len
);
1809 * Determine whether the file referenced by the file descriptor 'handle' can
1810 * be trusted, namely is it a file resident in the local file system.
1812 * The main method of operation is to perform operations on the file
1813 * descriptor so that an attempt to spoof the checks should fail, for
1814 * example renamimg the file from underneath us and/or changing where the
1815 * file lives from underneath us.
1817 * returns: -1 for error, indicating that we can not tell
1818 * 0 for file is definately not local, or it is an RFS link
1819 * 1 for file is local and can be trusted
1821 * Side effect: copies the stat information into the supplied buffer,
1822 * regardless of the type of file system the file resides.
1824 * Currently, the cases that we try to distinguish are RFS, AFS, NFS and
1825 * UFS, where the latter is considered a trusted file. We assume that the
1826 * caller has disabled link following and will detect an attempt to access
1827 * a file through an RFS link, except in the case the last component is
1828 * an RFS link. With link following disabled, the last component itself is
1829 * interpreted as a regular file if it is really an RFS link, so we
1830 * disallow the RFS link identified by group "symlink" and mode "IEXEC by
1831 * owner only". An AFS file is
1832 * detected by trying the VIOCIGETCELL ioctl, which is one of the few AFS
1833 * ioctls which operate on a file descriptor. Note, this AFS ioctl is
1834 * implemented in the cache manager, so the decision does not involve a
1835 * query with the AFS file server. An NFS file is detected by looking at
1836 * the major device number and seeing if it matches the known values for
1837 * MACH NSF/Sun OS 3.x or Sun OS 4.x.
1839 * Having the fstatvfs() system call would make this routine easier and
1842 * Note, in order to make the checks simpler, the file referenced by the
1843 * file descriptor can not be a BSD style symlink. Even with symlink
1844 * following of the last path component disabled, the attempt to open a
1845 * file which is a symlink will succeed, so we check for the BSD symlink
1846 * file type here. Also, the link following on/off and RFS file types
1847 * are only relevant in a MACH environment.
1850 #include <sys/viceioctl.h>
1853 #define SYMLINK_GRP 64
1856 local_file(int handle
, struct stat
* sinfo
)
1861 * dummies for the AFS ioctl
1863 struct ViceIoctl vdata
;
1865 #endif /* VIOCIGETCELL */
1867 if (fstat(handle
, &sb
) < 0)
1874 * If the following test succeeds, then the file referenced by
1875 * 'handle' is actually an RFS link, so we will not trust it.
1876 * See <sys/inode.h>.
1878 if (sb
.st_gid
== SYMLINK_GRP
1879 && (sb
.st_mode
& (S_IFMT
| S_IEXEC
| (S_IEXEC
>> 3) | (S_IEXEC
>> 6)))
1880 == (S_IFREG
| S_IEXEC
))
1885 * Do not trust BSD style symlinks either.
1887 if (S_ISLNK(sb
.st_mode
))
1892 * This is the VIOCIGETCELL ioctl, which takes an fd, not
1893 * a path name. If it succeeds, then the file is in AFS.
1895 * On failure, ENOTTY indicates that the file was not in
1896 * AFS; all other errors are pessimistically assumed to be
1897 * a temporary AFS error.
1900 vdata
.out_size
= sizeof(cellname
);
1901 vdata
.out
= cellname
;
1902 if (ioctl(handle
, VIOCIGETCELL
, (char *) &vdata
) != -1)
1904 if (errno
!= ENOTTY
)
1906 #endif /* VIOCIGETCELL */
1909 * Verify the file is not in NFS.
1911 * Our current implementation and Sun OS 3.x use major device
1912 * 255 for NFS files; Sun OS 4.x seems to use 130 (I have only
1913 * determined this empirically -- DLC). Without a fstatvfs()
1914 * system call, this will have to do for now.
1916 #if defined(__SVR4) || __NetBSD_Version__ > 299000900
1920 if (fstatvfs(handle
, &sf
) == -1)
1923 return strncmp(sf
.f_basetype
, "nfs", 3) != 0;
1925 return strncmp(sf
.f_fstypename
, "nfs", 3) != 0;
1928 #elif defined(__NetBSD__)
1931 if (fstatfs(handle
, &sf
) == -1)
1933 return strncmp(sf
.f_fstypename
, "nfs", 3) != 0;
1936 if (major(sb
.st_dev
) == 255 || major(sb
.st_dev
) == 130)
1944 * Companion routine for ensuring that a local file can be trusted. Compare
1945 * various pieces of the stat information to make sure that the file can be
1946 * trusted. Returns true for stat information which meets the criteria
1947 * for being trustworthy. The main paranoia is to prevent a hard link to
1948 * a root owned file. Since the link could be removed after the file is
1949 * opened, a simply fstat() can not be relied upon. The two stat buffers
1950 * for comparison should come from a stat() on the file name and a following
1951 * fstat() on the open file. Some of the following checks are also an
1952 * additional level of paranoia. Also, this test will fail (correctly) if
1953 * either or both of the stat structures have all fields zeroed; typically
1954 * due to a stat() failure.
1959 stat_info_ok(struct stat
* sb1
, struct stat
* sb2
)
1961 return (sb1
->st_ino
== sb2
->st_ino
&& /* Still the same file */
1962 sb1
->st_dev
== sb2
->st_dev
&& /* On the same device */
1963 sb1
->st_mode
== sb2
->st_mode
&& /* Perms (and type) same */
1964 S_ISREG(sb1
->st_mode
) && /* Only allow reg files */
1965 (sb1
->st_mode
& 077) == 0 && /* Owner only perms */
1966 sb1
->st_nlink
== sb2
->st_nlink
&& /* # hard links same... */
1967 sb1
->st_nlink
== 1 && /* and only 1 */
1968 sb1
->st_uid
== sb2
->st_uid
&& /* owner and ... */
1969 sb1
->st_gid
== sb2
->st_gid
&& /* group unchanged */
1970 sb1
->st_mtime
== sb2
->st_mtime
&& /* Unmodified between stats */
1971 sb1
->st_ctime
== sb2
->st_ctime
); /* Inode unchanged. Hopefully
1972 * a catch-all paranoid test */
1976 * Twiddle symbolic/RFS link following on/off. This is a no-op in a non
1977 * CMUCS/MACH environment. Also, the setmodes/getmodes interface is used
1978 * mainly because it is simpler than using table(2) directly.
1980 #include <sys/table.h>
1983 link_nofollow(int on
)
1985 static int modes
= -1;
1987 if (modes
== -1 && (modes
= getmodes()) == -1)
1990 return (setmodes(modes
| UMODE_NOFOLLOW
));
1991 return (setmodes(modes
));
1996 link_nofollow(int on __unused
)