]> granicus.if.org Git - transmission/commitdiff
(trunk, libT) #5517 'Don't create or add torrents with "../" at the beginning of...
authorJordan Lee <jordan@transmissionbt.com>
Sun, 27 Apr 2014 20:17:16 +0000 (20:17 +0000)
committerJordan Lee <jordan@transmissionbt.com>
Sun, 27 Apr 2014 20:17:16 +0000 (20:17 +0000)
libtransmission/metainfo.c

index 25f7ff91a5694fc3c1122c6fe6563ebcd9aa2b09..eb6ab0ac15eef0e5cfd48c0034302f1a62dfff7e 100644 (file)
 ****
 ***/
 
+
+#ifdef WIN32
+  #define PATH_DELIMITER_CHARS "/\\"
+#else
+  #define PATH_DELIMITER_CHARS "/"
+#endif
+
+static inline bool
+char_is_path_separator (char c)
+{
+  return strchr(PATH_DELIMITER_CHARS, c) != NULL;
+}
+
 char*
 tr_metainfoGetBasename (const tr_info * inf)
 {
@@ -39,7 +52,7 @@ tr_metainfoGetBasename (const tr_info * inf)
   char * ret = tr_strdup_printf ("%s.%16.16s", name, inf->hashString);
 
   for (i=0; i<name_len; ++i)
-    if (ret[i] == '/')
+    if (char_is_path_separator (ret[i]))
       ret[i] = '_';
 
   return ret;
@@ -60,11 +73,13 @@ getTorrentFilename (const tr_session * session, const tr_info * inf)
 ***/
 
 static bool
-path_is_suspicious (const char * path)
+path_component_is_suspicious (const char * component)
 {
-  return (path == NULL)
-      || (!strncmp (path, "../", 3))
-      || (strstr (path, "/../") != NULL);
+  return (component == NULL)
+      || (*component == '\0')
+      || (strpbrk (component, PATH_DELIMITER_CHARS) != NULL)
+      || (strcmp (component, ".") == 0)
+      || (strcmp (component, "..") == 0);
 }
 
 static bool
@@ -72,35 +87,41 @@ getfile (char ** setme, const char * root, tr_variant * path, struct evbuffer *
 {
   bool success = false;
 
+  *setme = NULL;
+
+  /* root's already been checked by caller */
+  assert (!path_component_is_suspicious (root));
+
   if (tr_variantIsList (path))
     {
       int i;
       const int n = tr_variantListSize (path);
 
+      success = true;
       evbuffer_drain (buf, evbuffer_get_length (buf));
       evbuffer_add (buf, root, strlen (root));
+
       for (i=0; i<n; i++)
         {
           size_t len;
           const char * str;
 
-          if (tr_variantGetStr (tr_variantListChild (path, i), &str, &len))
+          if (!tr_variantGetStr (tr_variantListChild (path, i), &str, &len) ||
+              path_component_is_suspicious (str))
             {
-              evbuffer_add (buf, TR_PATH_DELIMITER_STR, 1);
-              evbuffer_add (buf, str, len);
+              success = false;
+              break;
             }
-        }
 
-      *setme = tr_utf8clean ((char*)evbuffer_pullup (buf, -1), evbuffer_get_length (buf));
-      /* fprintf (stderr, "[%s]\n", *setme); */
-      success = true;
+          evbuffer_add (buf, TR_PATH_DELIMITER_STR, 1);
+          evbuffer_add (buf, str, len);
+        }
     }
 
-  if ((*setme != NULL) && path_is_suspicious (*setme))
+  if (success)
     {
-      tr_free (*setme);
-      *setme = NULL;
-      success = false;
+      *setme = tr_utf8clean ((char*)evbuffer_pullup (buf, -1), evbuffer_get_length (buf));
+      /*fprintf (stderr, "[%s]\n", *setme);*/
     }
 
   return success;
@@ -116,7 +137,14 @@ parseFiles (tr_info * inf, tr_variant * files, const tr_variant * length)
   if (tr_variantIsList (files)) /* multi-file mode */
     {
       tr_file_index_t i;
-      struct evbuffer * buf = evbuffer_new ();
+      struct evbuffer * buf;
+      const char * result;
+
+      if (path_component_is_suspicious (inf->name))
+        return "path";
+
+      buf = evbuffer_new ();
+      result = NULL;
 
       inf->isMultifile = 1;
       inf->fileCount = tr_variantListSize (files);
@@ -129,27 +157,40 @@ parseFiles (tr_info * inf, tr_variant * files, const tr_variant * length)
 
           file = tr_variantListChild (files, i);
           if (!tr_variantIsDict (file))
-            return "files";
+            {
+              result = "files";
+              break;
+            }
 
           if (!tr_variantDictFindList (file, TR_KEY_path_utf_8, &path))
             if (!tr_variantDictFindList (file, TR_KEY_path, &path))
-              return "path";
+              {
+                result = "path";
+                break;
+              }
 
           if (!getfile (&inf->files[i].name, inf->name, path, buf))
-            return "path";
+            {
+              result = "path";
+              break;
+            }
 
           if (!tr_variantDictFindInt (file, TR_KEY_length, &len))
-            return "length";
+            {
+              result = "length";
+              break;
+            }
 
           inf->files[i].length = len;
           inf->totalSize      += len;
         }
 
       evbuffer_free (buf);
+      return result;
     }
   else if (tr_variantGetInt (length, &len)) /* single-file mode */
     {
-      if (path_is_suspicious (inf->name))
+      if (path_component_is_suspicious (inf->name))
         return "path";
 
       inf->isMultifile      = 0;