Completed
Push — master ( 36647d...63fb17 )
by Lukas
24:17 queued 13:46
created

CheckSetupController::isPhpSupported()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
c 1
b 1
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Joas Schilling <[email protected]>
6
 * @author Lukas Reschke <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Robin McCorkell <[email protected]>
9
 * @author Roeland Jago Douma <[email protected]>
10
 *
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
namespace OC\Settings\Controller;
28
29
use GuzzleHttp\Exception\ClientException;
30
use OC\AppFramework\Http;
31
use OC\IntegrityCheck\Checker;
32
use OCP\AppFramework\Controller;
33
use OCP\AppFramework\Http\DataDisplayResponse;
34
use OCP\AppFramework\Http\DataResponse;
35
use OCP\AppFramework\Http\RedirectResponse;
36
use OCP\Http\Client\IClientService;
37
use OCP\IConfig;
38
use OCP\IL10N;
39
use OCP\ILogger;
40
use OCP\IRequest;
41
use OC_Util;
42
use OCP\IURLGenerator;
43
44
/**
45
 * @package OC\Settings\Controller
46
 */
47
class CheckSetupController extends Controller {
48
	/** @var IConfig */
49
	private $config;
50
	/** @var IClientService */
51
	private $clientService;
52
	/** @var \OC_Util */
53
	private $util;
54
	/** @var IURLGenerator */
55
	private $urlGenerator;
56
	/** @var IL10N */
57
	private $l10n;
58
	/** @var Checker */
59
	private $checker;
60
	/** @var ILogger */
61
	private $logger;
62
63
	/**
64
	 * @param string $AppName
65
	 * @param IRequest $request
66
	 * @param IConfig $config
67
	 * @param IClientService $clientService
68
	 * @param IURLGenerator $urlGenerator
69
	 * @param \OC_Util $util
70
	 * @param IL10N $l10n
71
	 * @param Checker $checker
72
	 * @param ILogger $logger
73
	 */
74
	public function __construct($AppName,
75
								IRequest $request,
76
								IConfig $config,
77
								IClientService $clientService,
78
								IURLGenerator $urlGenerator,
79
								\OC_Util $util,
80
								IL10N $l10n,
81
								Checker $checker,
82
								ILogger $logger) {
83
		parent::__construct($AppName, $request);
84
		$this->config = $config;
85
		$this->clientService = $clientService;
86
		$this->util = $util;
87
		$this->urlGenerator = $urlGenerator;
88
		$this->l10n = $l10n;
89
		$this->checker = $checker;
90
		$this->logger = $logger;
91
	}
92
93
	/**
94
	 * Checks if the ownCloud server can connect to the internet using HTTPS and HTTP
95
	 * @return bool
96
	 */
97
	private function isInternetConnectionWorking() {
98
		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
99
			return false;
100
		}
101
102
		$siteArray = ['www.nextcloud.com',
103
						'www.google.com',
104
						'www.github.com'];
105
106
		foreach($siteArray as $site) {
107
			if ($this->isSiteReachable($site)) {
108
				return true;
109
			}
110
		}
111
		return false;
112
	}
113
114
	/**
115
	* Chceks if the ownCloud server can connect to a specific URL using both HTTPS and HTTP
116
	* @return bool
117
	*/
118
	private function isSiteReachable($sitename) {
119
		$httpSiteName = 'http://' . $sitename . '/';
120
		$httpsSiteName = 'https://' . $sitename . '/';
121
122
		try {
123
			$client = $this->clientService->newClient();
124
			$client->get($httpSiteName);
125
			$client->get($httpsSiteName);
126
		} catch (\Exception $e) {
127
			$this->logger->logException($e, ['app' => 'internet_connection_check']);
128
			return false;
129
		}
130
		return true;
131
	}
132
133
	/**
134
	 * Checks whether a local memcache is installed or not
135
	 * @return bool
136
	 */
137
	private function isMemcacheConfigured() {
138
		return $this->config->getSystemValue('memcache.local', null) !== null;
139
	}
140
141
	/**
142
	 * Whether /dev/urandom is available to the PHP controller
143
	 *
144
	 * @return bool
145
	 */
146
	private function isUrandomAvailable() {
147
		if(@file_exists('/dev/urandom')) {
148
			$file = fopen('/dev/urandom', 'rb');
149
			if($file) {
150
				fclose($file);
151
				return true;
152
			}
153
		}
154
155
		return false;
156
	}
157
158
	/**
159
	 * Public for the sake of unit-testing
160
	 *
161
	 * @return array
162
	 */
163
	protected function getCurlVersion() {
164
		return curl_version();
165
	}
166
167
	/**
168
	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
169
	 * have multiple bugs which likely lead to problems in combination with
170
	 * functionality required by ownCloud such as SNI.
171
	 *
172
	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
173
	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
174
	 * @return string
175
	 */
176
	private function isUsedTlsLibOutdated() {
177
		// Appstore is disabled by default in EE
178
		$appStoreDefault = false;
179
		if (\OC_Util::getEditionString() === '') {
180
			$appStoreDefault = true;
181
		}
182
183
		// Don't run check when:
184
		// 1. Server has `has_internet_connection` set to false
185
		// 2. AppStore AND S2S is disabled
186
		if(!$this->config->getSystemValue('has_internet_connection', true)) {
187
			return '';
188
		}
189
		if(!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)
190
			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
191
			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
192
			return '';
193
		}
194
195
		$versionString = $this->getCurlVersion();
196
		if(isset($versionString['ssl_version'])) {
197
			$versionString = $versionString['ssl_version'];
198
		} else {
199
			return '';
200
		}
201
202
		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
203
		if(!$this->config->getSystemValue('appstoreenabled', $appStoreDefault)) {
204
			$features = (string)$this->l10n->t('Federated Cloud Sharing');
205
		}
206
207
		// Check if at least OpenSSL after 1.01d or 1.0.2b
208
		if(strpos($versionString, 'OpenSSL/') === 0) {
209
			$majorVersion = substr($versionString, 8, 5);
210
			$patchRelease = substr($versionString, 13, 6);
211
212
			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
213
				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
214
				return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
215
			}
216
		}
217
218
		// Check if NSS and perform heuristic check
219
		if(strpos($versionString, 'NSS/') === 0) {
220
			try {
221
				$firstClient = $this->clientService->newClient();
222
				$firstClient->get('https://www.owncloud.org/');
223
224
				$secondClient = $this->clientService->newClient();
225
				$secondClient->get('https://owncloud.org/');
226
			} catch (ClientException $e) {
0 ignored issues
show
Bug introduced by
The class GuzzleHttp\Exception\ClientException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
227
				if($e->getResponse()->getStatusCode() === 400) {
228
					return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
229
				}
230
			}
231
		}
232
233
		return '';
234
	}
235
236
	/**
237
	 * Whether the version is outdated
238
	 *
239
	 * @return bool
240
	 */
241
	protected function isPhpOutdated() {
242
		if (version_compare(PHP_VERSION, '5.5.0') === -1) {
243
			return true;
244
		}
245
246
		return false;
247
	}
248
249
	/**
250
	 * Whether the php version is still supported (at time of release)
251
	 * according to: https://secure.php.net/supported-versions.php
252
	 *
253
	 * @return array
254
	 */
255
	private function isPhpSupported() {
256
		return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
257
	}
258
259
	/**
260
	 * Check if the reverse proxy configuration is working as expected
261
	 *
262
	 * @return bool
263
	 */
264
	private function forwardedForHeadersWorking() {
265
		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
266
		$remoteAddress = $this->request->getRemoteAddress();
267
268
		if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
269
			return false;
270
		}
271
272
		// either not enabled or working correctly
273
		return true;
274
	}
275
276
	/**
277
	 * Checks if the correct memcache module for PHP is installed. Only
278
	 * fails if memcached is configured and the working module is not installed.
279
	 *
280
	 * @return bool
281
	 */
282
	private function isCorrectMemcachedPHPModuleInstalled() {
283
		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
284
			return true;
285
		}
286
287
		// there are two different memcached modules for PHP
288
		// we only support memcached and not memcache
289
		// https://code.google.com/p/memcached/wiki/PHPClientComparison
290
		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
291
	}
292
293
	/**
294
	 * @return RedirectResponse
295
	 */
296
	public function rescanFailedIntegrityCheck() {
297
		$this->checker->runInstanceVerification();
298
		return new RedirectResponse(
299
			$this->urlGenerator->linkToRoute('settings_admin')
300
		);
301
	}
302
303
	/**
304
	 * @NoCSRFRequired
305
	 * @return DataResponse
306
	 */
307
	public function getFailedIntegrityCheckFiles() {
308
		if(!$this->checker->isCodeCheckEnforced()) {
309
			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
310
		}
311
312
		$completeResults = $this->checker->getResults();
313
314
		if(!empty($completeResults)) {
315
			$formattedTextResponse = 'Technical information
316
=====================
317
The following list covers which files have failed the integrity check. Please read
318
the previous linked documentation to learn more about the errors and how to fix
319
them.
320
321
Results
322
=======
323
';
324
			foreach($completeResults as $context => $contextResult) {
325
				$formattedTextResponse .= "- $context\n";
326
327
				foreach($contextResult as $category => $result) {
328
					$formattedTextResponse .= "\t- $category\n";
329
					if($category !== 'EXCEPTION') {
330
						foreach ($result as $key => $results) {
331
							$formattedTextResponse .= "\t\t- $key\n";
332
						}
333
					} else {
334
						foreach ($result as $key => $results) {
335
							$formattedTextResponse .= "\t\t- $results\n";
336
						}
337
					}
338
339
				}
340
			}
341
342
			$formattedTextResponse .= '
343
Raw output
344
==========
345
';
346
			$formattedTextResponse .= print_r($completeResults, true);
347
		} else {
348
			$formattedTextResponse = 'No errors have been found.';
349
		}
350
351
352
		$response = new DataDisplayResponse(
353
			$formattedTextResponse,
354
			Http::STATUS_OK,
355
			[
356
				'Content-Type' => 'text/plain',
357
			]
358
		);
359
360
		return $response;
361
	}
362
363
	/**
364
	 * @return DataResponse
365
	 */
366
	public function check() {
367
		return new DataResponse(
368
			[
369
				'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
370
				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
371
				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
372
				'isUrandomAvailable' => $this->isUrandomAvailable(),
373
				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
374
				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
375
				'phpSupported' => $this->isPhpSupported(),
376
				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
377
				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
378
				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
379
				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
380
				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
381
			]
382
		);
383
	}
384
}
385