CVE-2018-6198: Improper Link Resolution Before File Access ('Link Following') (CWE-59)
value: w3m through 0.5.3 does not properly handle temporary files when the ~/.w3m directory is unwritable, which allows a local attacker to craft a symlink attack to overwrite arbitrary files.
CWE description: The product attempts to access a file based on the filename, but it does not properly prevent that filename from identifying a link or shortcut that resolves to an unintended resource.
CVSS v3 base score: 4.7
Published: 2018-01-25T03:29Z
Repository: https://github.com/tats/w3m
Language: C
Files changed: config.h.dist, config.h.in, configure, configure.ac, main.c, rc.c
Commit date: 2018-01-21 10:34:11 +0900
Commit message: Make temporary directory safely when ~/.w3m is unwritable

Diff: diff --git a/config.h.dist b/config.h.dist
index 0440927..4d9c119 100644
--- a/config.h.dist
+++ b/config.h.dist
@@ -208,10 +208,11 @@ typedef long clen_t;
 #define HAVE_PUTENV
 #define HAVE_SRAND48
 #define HAVE_SRANDOM
 #undef HAVE_GETPASSPHRASE
 #define HAVE_CHDIR
+#define HAVE_MKDTEMP
 
 
 #define SETJMP(env) sigsetjmp(env,1)
 #define LONGJMP(env,val) siglongjmp(env,val)
 #define JMP_BUF sigjmp_buf
diff --git a/config.h.in b/config.h.in
index 3b575e4..6ab3008 100644
--- a/config.h.in
+++ b/config.h.in
@@ -141,10 +141,11 @@ typedef long clen_t;
 #undef HAVE_PUTENV
 #undef HAVE_SRAND48
 #undef HAVE_SRANDOM
 #undef HAVE_GETPASSPHRASE
 #undef HAVE_CHDIR
+#undef HAVE_MKDTEMP
 #undef HAVE_SETPGRP
 #undef HAVE_SETLOCALE
 #undef HAVE_LANGINFO_CODESET
 
 #undef SETPGRP_VOID
diff --git a/configure b/configure
index 140f050..8699b43 100755
--- a/configure
+++ b/configure
@@ -8765,11 +8765,11 @@ cat >>confdefs.h <<_ACEOF
 #define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long
 _ACEOF
 
 
 
-for ac_func in strcasecmp strcasestr strchr memcpy strerror bcopy setpgrp chdir getcwd getwd readlink setenv putenv strtoll stroq atoll atoq symlink readlink lstat srand48 srandom getpassphrase waitpid setlocale
+for ac_func in strcasecmp strcasestr strchr memcpy strerror bcopy setpgrp chdir mkdtemp getcwd getwd readlink setenv putenv strtoll stroq atoll atoq symlink readlink lstat srand48 srandom getpassphrase waitpid setlocale
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
 if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
   cat >>confdefs.h <<_ACEOF
diff --git a/configure.ac b/configure.ac
index 320a6e2..ebb3282 100644
--- a/configure.ac
+++ b/configure.ac
@@ -148,11 +148,11 @@ dnl AC_FUNC_ALLOCA
 dnl AC_PROG_GCC_TRADITIONAL
 dnl AC_FUNC_MEMCMP
 dnl AC_FUNC_MMAP
 dnl AC_FUNC_SETVBUF_REVERSED
 dnl AC_FUNC_VPRINTF
-AC_CHECK_FUNCS(strcasecmp strcasestr strchr memcpy strerror bcopy setpgrp chdir getcwd getwd readlink setenv putenv strtoll stroq atoll atoq symlink readlink lstat srand48 srandom getpassphrase waitpid setlocale)
+AC_CHECK_FUNCS(strcasecmp strcasestr strchr memcpy strerror bcopy setpgrp chdir mkdtemp getcwd getwd readlink setenv putenv strtoll stroq atoll atoq symlink readlink lstat srand48 srandom getpassphrase waitpid setlocale)
 AC_FUNC_STRFTIME
 AC_FUNC_WAIT3
 AC_FUNC_SETPGRP
 case "$host_os" in
   *cygwin*)
diff --git a/main.c b/main.c
index a20b4a1..43e181c 100644
--- a/main.c
+++ b/main.c
@@ -5969,10 +5969,17 @@ w3m_exit(int i)
 #ifdef USE_NNTP
     disconnectNews();
 #endif
 #ifdef __MINGW32_VERSION
     WSACleanup();
+#endif
+#ifdef HAVE_MKDTEMP
+    if (no_rc_dir && tmp_dir != rc_dir)
+	if (rmdir(tmp_dir) != 0) {
+	    fprintf(stderr, "Can't remove temporary directory (%s)!\n", tmp_dir);
+	    exit(1);
+	}
 #endif
     exit(i);
 }
 
 DEFUN(execCmd, COMMAND, "Invoke w3m function(s)")
diff --git a/rc.c b/rc.c
index eb562c9..3fd84ef 100644
--- a/rc.c
+++ b/rc.c
@@ -1328,10 +1328,15 @@ init_rc(void)
     no_rc_dir = TRUE;
     if (((tmp_dir = getenv("TMPDIR")) == NULL || *tmp_dir == '\0') &&
 	((tmp_dir = getenv("TMP")) == NULL || *tmp_dir == '\0') &&
 	((tmp_dir = getenv("TEMP")) == NULL || *tmp_dir == '\0'))
 	tmp_dir = "/tmp";
+#ifdef HAVE_MKDTEMP
+    tmp_dir = mkdtemp(Strnew_m_charp(tmp_dir, "/w3m-XXXXXX", NULL)->ptr);
+    if (tmp_dir == NULL)
+	tmp_dir = rc_dir;
+#endif
     create_option_search_table();
     goto open_rc;
 }
 
 

Vulnerable code: for ac_func in strcasecmp strcasestr strchr memcpy strerror bcopy setpgrp chdir getcwd getwd readlink setenv putenv strtoll stroq atoll atoq symlink readlink lstat srand48 srandom getpassphrase waitpid setlocale
AC_CHECK_FUNCS(strcasecmp strcasestr strchr memcpy strerror bcopy setpgrp chdir getcwd getwd readlink setenv putenv strtoll stroq atoll atoq symlink readlink lstat srand48 srandom getpassphrase waitpid setlocale)
Fixed code: #define HAVE_MKDTEMP
#undef HAVE_MKDTEMP
for ac_func in strcasecmp strcasestr strchr memcpy strerror bcopy setpgrp chdir mkdtemp getcwd getwd readlink setenv putenv strtoll stroq atoll atoq symlink readlink lstat srand48 srandom getpassphrase waitpid setlocale
AC_CHECK_FUNCS(strcasecmp strcasestr strchr memcpy strerror bcopy setpgrp chdir mkdtemp getcwd getwd readlink setenv putenv strtoll stroq atoll atoq symlink readlink lstat srand48 srandom getpassphrase waitpid setlocale)
#endif
#ifdef HAVE_MKDTEMP
    if (no_rc_dir && tmp_dir != rc_dir)
	if (rmdir(tmp_dir) != 0) {
	    fprintf(stderr, "Can't remove temporary directory (%s)!\n", tmp_dir);
	    exit(1);
	}
#ifdef HAVE_MKDTEMP
    tmp_dir = mkdtemp(Strnew_m_charp(tmp_dir, "/w3m-XXXXXX", NULL)->ptr);
    if (tmp_dir == NULL)
	tmp_dir = rc_dir;
#endif