From f01aded72c38f0e18bc7ff67dee800e380251c8e Mon Sep 17 00:00:00 2001
From: Christian Persch <chpe@gnome.org>
Date: Mon, 11 Feb 2013 21:36:58 +0000
Subject: io: Implement strict load policy

Allow any file to load from data:, and any resource to load from other
resources. Only allow file: to load other file: URIs from below the path
of the base file. Any other loads are denied.

Bug #691708.
---
Index: librsvg-2.36.1/rsvg-base.c
===================================================================
--- librsvg-2.36.1.orig/rsvg-base.c	2012-03-26 14:25:08.000000000 +0200
+++ librsvg-2.36.1/rsvg-base.c	2013-11-26 16:07:42.481471848 +0100
@@ -25,6 +25,7 @@
 */
 
 #include "config.h"
+#define _GNU_SOURCE 1
 
 #include "rsvg.h"
 #include "rsvg-private.h"
@@ -1001,6 +1002,7 @@ void
 rsvg_handle_set_base_uri (RsvgHandle * handle, const char *base_uri)
 {
     gchar *uri;
+    GFile *file;
 
     g_return_if_fail (handle != NULL);
 
@@ -1012,11 +1014,10 @@ rsvg_handle_set_base_uri (RsvgHandle * h
     else
         uri = rsvg_get_base_uri_from_filename (base_uri);
 
-    if (uri) {
-        if (handle->priv->base_uri)
-            g_free (handle->priv->base_uri);
-        handle->priv->base_uri = uri;
-    }
+    file = g_file_new_for_uri (uri ? uri : "data:");
+    rsvg_handle_set_base_gfile (handle, file);
+    g_object_unref (file);
+    g_free (uri);
 }
 
 /**
@@ -2146,12 +2147,84 @@ _rsvg_handle_allow_load (RsvgHandle *han
                          const char *uri,
                          GError **error)
 {
-    RsvgLoadPolicy policy = handle->priv->load_policy;
+    RsvgHandlePrivate *priv = handle->priv;
+    GFile *base;
+    char *path, *dir;
+    char *scheme = NULL, *cpath = NULL, *cdir = NULL;
 
-    if (policy == RSVG_LOAD_POLICY_ALL_PERMISSIVE)
-        return TRUE;
+    g_assert (handle->priv->load_policy == RSVG_LOAD_POLICY_STRICT);
+
+    scheme = g_uri_parse_scheme (uri);
+
+    /* Not a valid URI */
+    if (scheme == NULL)
+        goto deny;
+
+    /* Allow loads of data: from any location */
+    if (g_str_equal (scheme, "data"))
+        goto allow;
+
+    /* No base to compare to? */
+    if (priv->base_gfile == NULL)
+        goto deny;
+
+    /* Deny loads from differing URI schemes */
+    if (!g_file_has_uri_scheme (priv->base_gfile, scheme))
+        goto deny;
+
+    /* resource: is allowed to load anything from other resources */
+    if (g_str_equal (scheme, "resource"))
+        goto allow;
 
+    /* Non-file: isn't allowed to load anything */
+    if (!g_str_equal (scheme, "file"))
+        goto deny;
+
+    base = g_file_get_parent (priv->base_gfile);
+    if (base == NULL)
+        goto deny;
+
+    dir = g_file_get_path (base);
+    g_object_unref (base);
+
+    /* FIXME portability */
+    cdir = canonicalize_file_name (dir);
+    g_free (dir);
+    if (cdir == NULL)
+        goto deny;
+
+    path = g_filename_from_uri (uri, NULL, NULL);
+    if (path == NULL)
+        goto deny;
+
+    /* FIXME portability */
+    cpath = canonicalize_file_name (path);
+    g_free (path);
+
+    if (cpath == NULL)
+        goto deny;
+
+    /* Now check that @cpath is below @cdir */
+    if (!g_str_has_prefix (cpath, cdir) ||
+        cpath[strlen (cdir)] != G_DIR_SEPARATOR)
+        goto deny;
+
+    /* Allow load! */
+
+ allow:
+    g_free (scheme);
+    free (cpath);
+    free (cdir);
     return TRUE;
+
+ deny:
+    g_free (scheme);
+    free (cpath);
+    free (cdir);
+
+    g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                 "File may not link to URI \"%s\"", uri);
+    return FALSE;
 }
 
 guint8* 
Index: librsvg-2.36.1/rsvg-io.c
===================================================================
--- librsvg-2.36.1.orig/rsvg-io.c	2012-03-26 14:25:08.000000000 +0200
+++ librsvg-2.36.1/rsvg-io.c	2013-11-26 16:07:25.021364586 +0100
@@ -79,7 +79,7 @@ rsvg_acquire_data_data (const char *uri,
     gboolean base64 = FALSE;
 
     g_assert (out_len != NULL);
-    g_assert (g_str_has_prefix (uri, "data:"));
+    g_assert (strncmp (uri, "data:", 5) == 0);
 
     mime_type = NULL;
     start = uri + 5;
Index: librsvg-2.36.1/rsvg-private.h
===================================================================
--- librsvg-2.36.1.orig/rsvg-private.h	2012-02-07 17:38:41.000000000 +0100
+++ librsvg-2.36.1/rsvg-private.h	2013-11-26 16:07:25.025364611 +0100
@@ -123,10 +123,10 @@ struct RsvgSaxHandler {
 };
 
 typedef enum {
-    RSVG_LOAD_POLICY_ALL_PERMISSIVE
+    RSVG_LOAD_POLICY_STRICT
 } RsvgLoadPolicy;
 
-#define RSVG_LOAD_POLICY_DEFAULT (RSVG_LOAD_POLICY_ALL_PERMISSIVE)
+#define RSVG_LOAD_POLICY_DEFAULT (RSVG_LOAD_POLICY_STRICT)
 
 struct RsvgHandlePrivate {
     RsvgHandleFlags flags;
