From 6a81c86eae423463af70298c792fe20dde606e96 Mon Sep 17 00:00:00 2001
From: Frederic Massart <fred@moodle.com>
Date: Fri, 5 Dec 2014 18:24:41 +0800
Subject: [PATCH] MDL-48495 curl: Limit usage of curl to HTTP and HTTPS
 protocols

---
 lib/filelib.php            | 18 ++++++------------
 lib/tests/filelib_test.php | 39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/lib/filelib.php b/lib/filelib.php
index 62c5662..b09bfe7 100644
--- a/lib/filelib.php
+++ b/lib/filelib.php
@@ -3166,14 +3166,6 @@ class curl {
      * @return resource The curl handle
      */
     private function apply_opt($curl, $options) {
-        // Some more security first.
-        if (defined('CURLOPT_PROTOCOLS')) {
-            $this->options['CURLOPT_PROTOCOLS'] = (CURLPROTO_HTTP | CURLPROTO_HTTPS);
-        }
-        if (defined('CURLOPT_REDIR_PROTOCOLS')) {
-            $this->options['CURLOPT_REDIR_PROTOCOLS'] = (CURLPROTO_HTTP | CURLPROTO_HTTPS);
-        }
-
         // Clean up
         $this->cleanopt();
         // set cookie
@@ -3235,12 +3227,14 @@ class curl {
             $this->options['CURLOPT_FOLLOWLOCATION'] = 0;
         }
 
+        // Limit the protocols to HTTP and HTTPS.
+        if (defined('CURLOPT_PROTOCOLS')) {
+            $this->options['CURLOPT_PROTOCOLS'] = (CURLPROTO_HTTP | CURLPROTO_HTTPS);
+            $this->options['CURLOPT_REDIR_PROTOCOLS'] = (CURLPROTO_HTTP | CURLPROTO_HTTPS);
+        }
+
         // Set options.
         foreach($this->options as $name => $val) {
-            if ($name === 'CURLOPT_PROTOCOLS' or $name === 'CURLOPT_REDIR_PROTOCOLS') {
-                // These can not be changed, sorry.
-                continue;
-            }
             if ($name === 'CURLOPT_FOLLOWLOCATION' and $this->emulateredirects) {
                 // The redirects are emulated elsewhere.
                 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 0);
diff --git a/lib/tests/filelib_test.php b/lib/tests/filelib_test.php
index 004dc8b..180c9d8 100644
--- a/lib/tests/filelib_test.php
+++ b/lib/tests/filelib_test.php
@@ -504,6 +504,45 @@ class core_filelib_testcase extends advanced_testcase {
         $this->assertSame('OK', $contents);
     }
 
+    public function test_curl_protocols() {
+
+        // HTTP and HTTPS requests were verified in previous requests. Now check
+        // that we can selectively disable some protocols.
+        $curl = new curl();
+
+        // Other protocols than HTTP(S) are disabled by default.
+        $testurl = 'file:///';
+        $curl->get($testurl);
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        $testurl = 'ftp://nowhere';
+        $curl->get($testurl);
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        $testurl = 'telnet://somewhere';
+        $curl->get($testurl);
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        // Protocols are also disabled during redirections.
+        $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
+        $curl->get($testurl, array('proto' => 'file'));
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
+        $curl->get($testurl, array('proto' => 'ftp'));
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+
+        $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
+        $curl->get($testurl, array('proto' => 'telnet'));
+        $this->assertNotEmpty($curl->error);
+        $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
+    }
+
     /**
      * Testing prepare draft area
      *
-- 
1.9.1

