about summary refs log tree commit diff stats
path: root/src/LYUtils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/LYUtils.c')
-rw-r--r--src/LYUtils.c127
1 files changed, 108 insertions, 19 deletions
diff --git a/src/LYUtils.c b/src/LYUtils.c
index 7f926295..9a2432a2 100644
--- a/src/LYUtils.c
+++ b/src/LYUtils.c
@@ -1,6 +1,4 @@
 #include <HTUtils.h>
-#include <tcp.h>
-#include <ctype.h>
 #include <HTParse.h>
 #include <HTAccess.h>
 #include <HTCJK.h>
@@ -59,8 +57,6 @@
 #include <LYStyle.h>
 #endif
 
-#undef hline   /* FIXME: this is a curses feature used as a variable here */
-
 #ifdef SVR4_BSDSELECT
 extern int BSDselect PARAMS((int nfds, fd_set * readfds, fd_set * writefds,
 			     fd_set * exceptfds, struct timeval * timeout));
@@ -88,8 +84,6 @@ extern int BSDselect PARAMS((int nfds, fd_set * readfds, fd_set * writefds,
 #endif /* __FreeBSD__ || __bsdi__ */
 #endif /* !UTMP_FILE */
 
-#define FREE(x) if (x) {free(x); x = NULL;}
-
 extern HTkcode kanji_code;
 extern BOOLEAN LYHaveCJKCharacterSet;
 extern HTCJKlang HTCJK;
@@ -4747,6 +4741,37 @@ PUBLIC CONST char * Home_Dir NOARGS
 }
 
 /*
+ * Return a pointer to the final leaf of the given pathname, If no pathname
+ * separators are found, returns the original pathname.  The leaf may be
+ * empty.
+ */
+PUBLIC char *LYPathLeaf ARGS1(char *, pathname)
+{
+    char *leaf;
+#ifdef UNIX
+    if ((leaf = strrchr(pathname, '/')) != 0) {
+	leaf++;
+    }
+#else
+#ifdef VMS
+    if ((leaf = strrchr(pathname, ']')) == 0)
+    	leaf = strrchr(pathname, ':');
+    if (leaf != 0)
+	leaf++;
+#else
+    int n;
+    for (leaf = 0, n = strlen(pathname)-1; n >= 0; n--) {
+	if (strchr("\\/:", pathname[n]) != 0) {
+	    leaf = pathname + n;
+	    break;
+	}
+    }
+#endif
+#endif
+    return (leaf != 0) ? leaf : pathname;
+}
+
+/*
  *  This function checks the acceptability of file paths that
  *  are intended to be off the home directory.	The file path
  *  should be passed in fbuffer, together with the size of the
@@ -5303,11 +5328,6 @@ Cambridge, MA 02139, USA.  */
 
 #include <sys/types.h>
 #include <errno.h>
-#ifdef STDC_HEADERS
-#include <stdlib.h>
-#else
-extern int errno;
-#endif /* STDC_HEADERS */
 
 #if defined(STDC_HEADERS) || defined(USG)
 #include <string.h>
@@ -5389,6 +5409,71 @@ int remove ARGS1(char *, name)
 
 #ifdef UNIX
 /*
+ * Verify if this is really a file, not accessed by a link, except for the
+ * special case of its directory being pointed to by a link from a directory
+ * owned by root and not writable by other users.
+ */
+PRIVATE BOOL IsOurFile ARGS1(char *, name)
+{
+    struct stat data;
+
+    if (lstat(name, &data) == 0
+    && S_ISREG(data.st_mode)
+    && data.st_nlink == 1
+    && data.st_uid == getuid()) {
+	int linked = 0;
+#if HAVE_LSTAT
+	char *path = 0;
+	char *leaf;
+
+	StrAllocCopy(path, name);
+	do {
+	    if ((leaf = LYPathLeaf(path)) != path)
+		*--leaf = '\0';	/* write a null on the '/' */
+	    if (lstat(*path ? path : "/", &data) != 0) {
+	    	break;
+	    }
+	    /*
+	     * If we find a symbolic link, it has to be in a directory that's
+	     * protected.  Otherwise someone could have switched it to point
+	     * to one of the real user's files.
+	     */
+	    if (S_ISLNK(data.st_mode)) {
+		if (!linked) {
+		    linked++;
+		} else {	/* a link-to-link is a little hard to digest */
+		    break;
+		}
+	    } else if (S_ISDIR(data.st_mode)) {
+		if (linked) {
+		    if (--linked == 0) {
+			/*
+			 * We assume that a properly-configured system has the
+			 * unwritable directories owned by root.  This is not
+			 * necessarily so (bin, news, etc., may), but the only
+			 * uid we can count on is 0.  It would be nice to add a
+			 * check for the gid also, but that wouldn't be
+			 * portable.
+			 */
+			if (data.st_uid != 0
+			 || data.st_mode & S_IWOTH) {
+			    linked = 1;
+			    break;
+			}
+		    }
+		}
+	    } else if (linked) {
+		break;
+	    }
+	} while (leaf != path);
+	free(path);
+#endif
+	return !linked;
+    }
+    return FALSE;
+}
+
+/*
  * Open a file that we don't want other users to see.
  */
 PRIVATE FILE *OpenHiddenFile ARGS2(char *, name, char *, mode)
@@ -5401,20 +5486,24 @@ PRIVATE FILE *OpenHiddenFile ARGS2(char *, name, char *, mode)
      * that no one has an existing file or link that they happen to own.
      */
     if (*mode == 'w') {
-	struct stat sb;
 	int fd = open(name, O_CREAT|O_EXCL|O_WRONLY, HIDE_CHMOD);
+	if (fd < 0
+	 && errno == EEXIST
+	 && IsOurFile(name)) {
+	    remove(name);
+	    /* FIXME: there's a race at this point if directory is open */
+	    fd = open(name, O_CREAT|O_EXCL|O_WRONLY, HIDE_CHMOD);
+	}
 	if (fd >= 0) {
 	    fp = fdopen(fd, mode);
 	}
-	else if (errno == EEXIST
-	 && stat(name, &sb) == 0
-	 && sb.st_uid == getuid()
-	 && chmod(name, HIDE_CHMOD) == 0
-	 && (fd = open(name, O_TRUNC|O_WRONLY, HIDE_CHMOD)) >= 0)
-	    fp = fdopen(fd, mode);
     }
     else
 #endif
+    if (*mode == 'a'
+     && IsOurFile(name)
+     && chmod(name, HIDE_CHMOD) == 0)
+	fp = fopen(name, mode);
     /*
      * This is less stringent, but reasonably portable.  For new files, the
      * umask will suffice; however if the file already exists we'll change
@@ -5424,7 +5513,7 @@ PRIVATE FILE *OpenHiddenFile ARGS2(char *, name, char *, mode)
      *
      * This won't work properly if the user is root, since the chmod succeeds.
      */
-    {
+    else if (*mode != 'a') {
 	int save = umask(HIDE_UMASK);
 	if (chmod(name, HIDE_CHMOD) == 0 || errno == ENOENT)
 	    fp = fopen(name, mode);