diff --git a/htdocs/admin/system/security.php b/htdocs/admin/system/security.php
index 0cf70f2bbaa..05cf9dd1b8e 100644
--- a/htdocs/admin/system/security.php
+++ b/htdocs/admin/system/security.php
@@ -50,15 +50,21 @@ llxHeader();
print load_fiche_titre($langs->trans("Security"), '', 'title_setup');
print ''.$langs->trans("YouMayFindSecurityAdviceHere", 'hhttps://wiki.dolibarr.org/index.php/Security_information').' ('.$langs->trans("Reload").')
';
+print '
';
-// Recupere la version de PHP
+print load_fiche_titre($langs->trans("PHPSetup"), '', '');
+
+// Get version of PHP
$phpversion = version_php();
-print "
PHP - ".$langs->trans("Version").": ".$phpversion."
\n";
+print "PHP - ".$langs->trans("Version").": ".$phpversion."
\n";
-// Recupere la version du serveur web
+// Get versionof web server
print "
Web server - ".$langs->trans("Version").": ".$_SERVER["SERVER_SOFTWARE"]."
\n";
print '
';
+print "PHP safe_mode = ".(ini_get('safe_mode') ? ini_get('safe_mode') : yn(0))."
\n";
+print "PHP open_basedir = ".(ini_get('open_basedir') ? ini_get('open_basedir') : yn(0))."
\n";
+print '
';
print load_fiche_titre($langs->trans("ConfigFile"), '', '');
@@ -73,6 +79,7 @@ print load_fiche_titre($langs->trans("PermissionsOnFiles"), '', '');
print ''.$langs->trans("PermissionOnFileInWebRoot").': ';
// TODO
+print 'TODO';
print '
';
@@ -126,6 +133,8 @@ print ''.$langs->trans("AntivirusEnabledOnUpload").': ';
// TODO
print '
';
+print '
';
+
print ''.$langs->trans("SecurityAudit").': ';
// TODO Disabled or enabled ?
print '
';
diff --git a/htdocs/core/lib/geturl.lib.php b/htdocs/core/lib/geturl.lib.php
index e6e20692317..908884ee059 100644
--- a/htdocs/core/lib/geturl.lib.php
+++ b/htdocs/core/lib/geturl.lib.php
@@ -1,5 +1,5 @@
+/* Copyright (C) 2008-2020 Laurent Destailleur
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,12 +26,14 @@
*
* @param string $url URL to call.
* @param string $postorget 'POST', 'GET', 'HEAD', 'PUT', 'PUTALREADYFORMATED', 'POSTALREADYFORMATED', 'DELETE'
- * @param string $param Parameters of URL (x=value1&y=value2) or may be a formated content with PUTALREADYFORMATED
- * @param integer $followlocation 1=Follow location, 0=Do not follow
+ * @param string $param Parameters of URL (x=value1&y=value2) or may be a formated content with $postorget='PUTALREADYFORMATED'
+ * @param integer $followlocation 0=Do not follow, 1=Follow location.
* @param string[] $addheaders Array of string to add into header. Example: ('Accept: application/xrds+xml', ....)
+ * @param string[] $allowedschemes List of schemes that are allowed ('http' + 'https' only by default)
+ * @param int $localurl 0=Only external URL are possible, 1=Only local URL, 2=Both external and local URL are allowed.
* @return array Returns an associative array containing the response from the server array('content'=>response,'curl_error_no'=>errno,'curl_error_msg'=>errmsg...)
*/
-function getURLContent($url, $postorget = 'GET', $param = '', $followlocation = 1, $addheaders = array())
+function getURLContent($url, $postorget = 'GET', $param = '', $followlocation = 1, $addheaders = array(), $allowedschemes = array('http', 'https'), $localurl = 0)
{
//declaring of global variables
global $conf;
@@ -50,13 +52,14 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
print $USE_PROXY."-".$gv_ApiErrorURL."
";
print $nvpStr;
exit;*/
- curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'Dolibarr geturl function');
- @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, ($followlocation ?true:false)); // We use @ here because this may return warning if safe mode is on or open_basedir is on
+ // We use @ here because this may return warning if safe mode is on or open_basedir is on (following location is forbidden when safe mode is on).
+ // We force value to false so we will manage redirection ourself later.
+ @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
- if (count($addheaders)) curl_setopt($ch, CURLOPT_HTTPHEADER, $addheaders);
+ if (is_array($addheaders) && count($addheaders)) curl_setopt($ch, CURLOPT_HTTPHEADER, $addheaders);
curl_setopt($ch, CURLINFO_HEADER_OUT, true); // To be able to retrieve request header and log it
// By default use tls decied by PHP.
@@ -64,25 +67,33 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
if (!empty($conf->global->MAIN_CURL_SSLVERSION)) curl_setopt($ch, CURLOPT_SSLVERSION, $conf->global->MAIN_CURL_SSLVERSION);
//curl_setopt($ch, CURLOPT_SSLVERSION, 6); for tls 1.2
- //turning off the server and peer verification(TrustManager Concept).
+ // Turning off the server and peer verification(TrustManager Concept).
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ // Restrict use to some protocols only
+ $protocols = 0;
+ if (is_array($allowedschemes)) {
+ foreach($allowedschemes as $allowedscheme) {
+ if ($allowedscheme == 'http') $protocols |= CURLPROTO_HTTP;
+ if ($allowedscheme == 'https') $protocols |= CURLPROTO_HTTPS;
+ }
+ curl_setopt($ch, CURLOPT_PROTOCOLS, $protocols);
+ curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, $protocols);
+ }
+
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, empty($conf->global->MAIN_USE_CONNECT_TIMEOUT) ? 5 : $conf->global->MAIN_USE_CONNECT_TIMEOUT);
curl_setopt($ch, CURLOPT_TIMEOUT, empty($conf->global->MAIN_USE_RESPONSE_TIMEOUT) ? 30 : $conf->global->MAIN_USE_RESPONSE_TIMEOUT);
//curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true); // PHP 5.5
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // We want response
- if ($postorget == 'POST')
- {
+ if ($postorget == 'POST') {
curl_setopt($ch, CURLOPT_POST, 1); // POST
curl_setopt($ch, CURLOPT_POSTFIELDS, $param); // Setting param x=a&y=z as POST fields
- } elseif ($postorget == 'POSTALREADYFORMATED')
- {
+ } elseif ($postorget == 'POSTALREADYFORMATED') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); // HTTP request is 'POST' but param string is taken as it is
curl_setopt($ch, CURLOPT_POSTFIELDS, $param); // param = content of post, like a xml string
- } elseif ($postorget == 'PUT')
- {
+ } elseif ($postorget == 'PUT') {
$array_param = null;
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); // HTTP request is 'PUT'
if (!is_array($param)) parse_str($param, $array_param);
@@ -91,32 +102,54 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
$array_param = $param;
}
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($array_param)); // Setting param x=a&y=z as PUT fields
- } elseif ($postorget == 'PUTALREADYFORMATED')
- {
+ } elseif ($postorget == 'PUTALREADYFORMATED') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); // HTTP request is 'PUT'
curl_setopt($ch, CURLOPT_POSTFIELDS, $param); // param = content of post, like a xml string
- } elseif ($postorget == 'HEAD')
- {
+ } elseif ($postorget == 'HEAD') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD'); // HTTP request is 'HEAD'
curl_setopt($ch, CURLOPT_NOBODY, true);
- } elseif ($postorget == 'DELETE')
- {
+ } elseif ($postorget == 'DELETE') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); // POST
} else {
curl_setopt($ch, CURLOPT_POST, 0); // GET
}
//if USE_PROXY constant set at begin of this method.
- if ($USE_PROXY)
- {
+ if ($USE_PROXY) {
dol_syslog("getURLContent set proxy to ".$PROXY_HOST.":".$PROXY_PORT." - ".$PROXY_USER.":".$PROXY_PASS);
//curl_setopt ($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); // Curl 7.10
curl_setopt($ch, CURLOPT_PROXY, $PROXY_HOST.":".$PROXY_PORT);
if ($PROXY_USER) curl_setopt($ch, CURLOPT_PROXYUSERPWD, $PROXY_USER.":".$PROXY_PASS);
}
- //getting response from server
- $response = curl_exec($ch);
+ $newUrl = $url;
+ $maxRedirection = 5;
+ $info = array();
+
+ do
+ {
+ if ($maxRedirection < 1) break;
+
+ curl_setopt($ch, CURLOPT_URL, $newUrl);
+
+ // TODO Test for $localurl on $newUrl
+ // Parse $newUrl, check server address, then set parsed value with CURLOPT_CONNECT_TO
+
+ // Getting response from server
+ $response = curl_exec($ch);
+
+ $info = curl_getinfo($ch); // Reading of request must be done after sending request
+ $http_code = $info['http_code'];
+ if ($followlocation && ($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307)) {
+ $newUrl = $info['redirect_url'];
+ $maxRedirection--;
+ // TODO Use $info['local_ip'] and $info['primary_ip'] ?
+ continue;
+ } else {
+ $http_code = 0;
+ }
+ }
+ while($http_code);
$request = curl_getinfo($ch, CURLINFO_HEADER_OUT); // Reading of request must be done after sending request
@@ -136,7 +169,7 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
dol_syslog("getURLContent response array is ".join(',', $rep));
} else {
- $info = curl_getinfo($ch);
+ //$info = curl_getinfo($ch);
// Ad keys to $rep
$rep = $info;
@@ -148,11 +181,11 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
$rep['content'] = $response;
$rep['curl_error_no'] = '';
$rep['curl_error_msg'] = '';
-
- //closing the curl
- curl_close($ch);
}
+ //closing the curl
+ curl_close($ch);
+
return $rep;
}
diff --git a/test/phpunit/SecurityTest.php b/test/phpunit/SecurityTest.php
index 4430b377b26..b4bdf1d89ab 100644
--- a/test/phpunit/SecurityTest.php
+++ b/test/phpunit/SecurityTest.php
@@ -383,4 +383,34 @@ class SecurityTest extends PHPUnit\Framework\TestCase
$result=restrictedArea($user, 'societe');
$this->assertEquals(1, $result);
}
+
+
+ /**
+ * testGetRandomPassword
+ *
+ * @return number
+ */
+ public function testGetURLContent()
+ {
+ global $conf;
+ include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
+
+ $url = 'ftp://aaaa';
+ $tmp = getURLContent($url);
+ print __METHOD__." url=".$url."\n";
+ $this->assertGreaterThan(0, strpos($tmp['curl_error_msg'], 'not supported')); // Test error if return does not contains 'not supported'
+
+ $url = 'https://www.dolibarr.fr'; // This is a redirect 301 page
+ $tmp = getURLContent($url, 'GET', '', 0); // We do NOT follow
+ print __METHOD__." url=".$url."\n";
+ $this->assertEquals(301, $tmp['http_code'], 'GET url 301 without following -> 301');
+
+ $url = 'https://www.dolibarr.fr'; // This is a redirect 301 page
+ $tmp = getURLContent($url); // We DO follow
+ print __METHOD__." url=".$url."\n";
+ //var_dump($tmp);
+ $this->assertEquals(200, $tmp['http_code'], 'GET url 301 with following -> 200'); // Test error if return does not contains 'not supported'
+
+ return 0;
+ }
}