diff dmd/root/root.c @ 1640:9bf06e02070b

Merge DMD 1.057.
author Christian Kamm <kamm incasoftware de>
date Mon, 08 Mar 2010 21:39:20 +0100
parents def7a1d494fd
children
line wrap: on
line diff
--- a/dmd/root/root.c	Mon Mar 08 20:09:18 2010 +0100
+++ b/dmd/root/root.c	Mon Mar 08 21:39:20 2010 +0100
@@ -1,8 +1,8 @@
 
-// Copyright (c) 1999-2009 by Digital Mars
+// Copyright (c) 1999-2010 by Digital Mars
 // All Rights Reserved
 // written by Walter Bright
-// www.digitalmars.com
+// http://www.digitalmars.com
 // License for redistribution is by either the Artistic License
 // in artistic.txt, or the GNU General Public License in gnu.txt.
 // See the included readme.txt for details.
@@ -14,6 +14,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <limits.h>
 #include <string.h>
 #include <stdint.h>
 #include <assert.h>
@@ -819,6 +820,91 @@
     return NULL;
 }
 
+
+/*************************************
+ * Search Path for file in a safe manner.
+ *
+ * Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
+ * ('Path Traversal') attacks.
+ *	http://cwe.mitre.org/data/definitions/22.html
+ * More info:
+ *	https://www.securecoding.cert.org/confluence/display/seccode/FIO02-C.+Canonicalize+path+names+originating+from+untrusted+sources
+ * Returns:
+ *	NULL	file not found
+ *	!=NULL	mem.malloc'd file name
+ */
+
+char *FileName::safeSearchPath(Array *path, const char *name)
+{
+#if _WIN32
+    /* Disallow % / \ : and .. in name characters
+     */
+    for (const char *p = name; *p; p++)
+    {
+	char c = *p;
+	if (c == '\\' || c == '/' || c == ':' || c == '%' ||
+	    (c == '.' && p[1] == '.'))
+	{
+	    return NULL;
+	}
+    }
+
+    return FileName::searchPath(path, name, 0);
+#elif POSIX
+    /* Even with realpath(), we must check for // and disallow it
+     */
+    for (const char *p = name; *p; p++)
+    {
+	char c = *p;
+	if (c == '/' && p[1] == '/')
+	{
+	    return NULL;
+	}
+    }
+
+    if (path)
+    {	unsigned i;
+
+	/* Each path is converted to a cannonical name and then a check is done to see
+	 * that the searched name is really a child one of the the paths searched.
+	 */
+	for (i = 0; i < path->dim; i++)
+	{
+	    char *cname = NULL;
+	    char *cpath = canonicalName((char *)path->data[i]);
+	    //printf("FileName::safeSearchPath(): name=%s; path=%s; cpath=%s\n",
+	    //	    name, (char *)path->data[i], cpath);
+	    if (cpath == NULL)
+		goto cont;
+	    cname = canonicalName(combine(cpath, name));
+	    //printf("FileName::safeSearchPath(): cname=%s\n", cname);
+	    if (cname == NULL)
+		goto cont;
+	    //printf("FileName::safeSearchPath(): exists=%i "
+	    //	    "strncmp(cpath, cname, %i)=%i\n", exists(cname),
+	    //	    strlen(cpath), strncmp(cpath, cname, strlen(cpath)));
+	    // exists and name is *really* a "child" of path
+	    if (exists(cname) && strncmp(cpath, cname, strlen(cpath)) == 0)
+	    {
+		free(cpath);
+		char *p = mem.strdup(cname);
+		free(cname);
+		return p;
+	    }
+cont:
+	    if (cpath)
+		free(cpath);
+	    if (cname)
+		free(cname);
+	}
+    }
+    return NULL;
+#else
+    assert(0);
+#endif
+}
+
+
 int FileName::exists(const char *name)
 {
 #if POSIX
@@ -886,6 +972,52 @@
     }
 }
 
+
+/******************************************
+ * Return canonical version of name in a malloc'd buffer.
+ * This code is high risk.
+ */
+char *FileName::canonicalName(const char *name)
+{
+#if linux
+    // Lovely glibc extension to do it for us
+    return canonicalize_file_name(name);
+#elif POSIX
+  #if _POSIX_VERSION >= 200809L || defined (linux)
+    // NULL destination buffer is allowed and preferred
+    return realpath(name, NULL);
+  #else
+    char *cname = NULL;
+    #if PATH_MAX
+	/* PATH_MAX must be defined as a constant in <limits.h>,
+	 * otherwise using it is unsafe due to TOCTOU
+	 */
+	size_t path_max = (size_t)PATH_MAX;
+	if (path_max > 0)
+	{
+	    /* Need to add one to PATH_MAX because of realpath() buffer overflow bug:
+	     * http://isec.pl/vulnerabilities/isec-0011-wu-ftpd.txt
+	     */
+	    cname = (char *)malloc(path_max + 1);
+	    if (cname == NULL)
+		return NULL;
+	}
+    #endif
+    return realpath(name, cname);
+  #endif
+#elif _WIN32
+    /* Apparently, there is no good way to do this on Windows.
+     * GetFullPathName isn't it.
+     */
+    assert(0);
+    return NULL;
+#else
+    assert(0);
+    return NULL;
+#endif
+}
+
+
 /****************************** File ********************************/
 
 File::File(FileName *n)