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; + } }