[tz] [PATCH] Fix some 'zic' issues when run as root.

Paul Eggert eggert at CS.UCLA.EDU
Fri Jun 27 04:22:12 UTC 2014


* zic.c: Use angle brackets for <sys/stat.h>.
(dolink): Don't try to link from a directory, as this can
cause trouble on some systems when superuser.
(dolink, itsdir): Rewrite to avoid use of 'access', which is
problematic when setuid.
(itsdir): If S_ISDIR is defined, use plain 'stat' and S_ISDIR;
that's more reliable.  Return -1 if trouble; all callers changed.
(mkdirs): Simplify by using mkdir-itsdir rather than
itsdir-mkdir-itsdir.  Don't assume strerror preserves errno.
---
 zic.c | 71 +++++++++++++++++++++++++++++++++++++++----------------------------
 1 file changed, 41 insertions(+), 30 deletions(-)

diff --git a/zic.c b/zic.c
index 9c22e7a..f18dba7 100644
--- a/zic.c
+++ b/zic.c
@@ -23,7 +23,7 @@ typedef int_fast64_t	zic_t;
 #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
 
 #if HAVE_SYS_STAT_H
-#include "sys/stat.h"
+#include <sys/stat.h>
 #endif
 #ifdef S_IRUSR
 #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
@@ -665,6 +665,7 @@ dolink(const char *const fromfield, const char *const tofield)
 {
 	register char *	fromname;
 	register char *	toname;
+	register int fromisdir;
 
 	namecheck(tofield);
 	if (fromfield[0] == '/')
@@ -685,10 +686,16 @@ dolink(const char *const fromfield, const char *const tofield)
 	** We get to be careful here since
 	** there's a fair chance of root running us.
 	*/
-	if (!itsdir(toname))
+	fromisdir = itsdir(fromname);
+	if (fromisdir) {
+		int err = fromisdir < 0 ? errno : EPERM;
+		fprintf(stderr, _("%s: link from %s failed: %s"),
+			progname, fromname, strerror(err));
+		exit(EXIT_FAILURE);
+	}
+	if (itsdir(toname) <= 0)
 		(void) remove(toname);
-	if (link(fromname, toname) != 0
-	    && access(fromname, F_OK) == 0 && !itsdir(fromname)) {
+	if (link(fromname, toname) != 0) {
 		int	result;
 
 		if (mkdirs(toname) != 0)
@@ -793,17 +800,24 @@ static const zic_t max_time = -1 - ((zic_t) -1 << (TIME_T_BITS_IN_FILE - 1));
 
 static const zic_t big_bang_time = BIG_BANG;
 
+/* Return 1 if NAME is a directory, 0 if it's something else, -1 if trouble.  */
 static int
 itsdir(const char *const name)
 {
-	register char *	myname;
-	register int	accres;
-
-	myname = ecpyalloc(name);
-	myname = ecatalloc(myname, "/.");
-	accres = access(myname, F_OK);
-	free(myname);
-	return accres == 0;
+	struct stat st;
+	int res = stat(name, &st);
+	if (res != 0)
+		return res;
+#ifdef S_ISDIR
+	return S_ISDIR(st.st_mode) != 0;
+#else
+	{
+		char *nameslashdot = ecatalloc(ecpyalloc(name), "/.");
+		res = stat(nameslashdot, &st);
+		free(nameslashdot);
+		return res == 0;
+	}
+#endif
 }
 
 /*
@@ -1620,7 +1634,7 @@ writezone(const char *const name, const char *const string, char version)
 	/*
 	** Remove old file, if any, to snap links.
 	*/
-	if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) {
+	if (itsdir(fullname) <= 0 && remove(fullname) != 0 && errno != ENOENT) {
 		const char *e = strerror(errno);
 
 		(void) fprintf(stderr, _("%s: Can't remove %s: %s\n"),
@@ -2891,23 +2905,20 @@ mkdirs(char *argname)
 				continue;
 		}
 #endif
-		if (!itsdir(name)) {
-			/*
-			** It doesn't seem to exist, so we try to create it.
-			** Creation may fail because of the directory being
-			** created by some other multiprocessor, so we get
-			** to do extra checking.
-			*/
-			if (mkdir(name, MKDIR_UMASK) != 0) {
-				const char *e = strerror(errno);
-
-				if (errno != EEXIST || !itsdir(name)) {
-					(void) fprintf(stderr,
-_("%s: Can't create directory %s: %s\n"),
-						progname, name, e);
-					free(name);
-					return -1;
-				}
+		/*
+		** Try to create it.  It's OK if creation fails because
+		** the directory already exists, perhaps because some
+		** other process just created it.
+		*/
+		if (mkdir(name, MKDIR_UMASK) != 0) {
+			int err = errno;
+			if (itsdir(name) <= 0) {
+				(void) fprintf(stderr,
+					       _("%s: Can't create directory"
+						 " %s: %s\n"),
+					       progname, name, strerror(err));
+				free(name);
+				return -1;
 			}
 		}
 		*cp = '/';
-- 
1.9.1



More information about the tz mailing list