diff --git a/admin/tool/mobile/classes/api.php b/admin/tool/mobile/classes/api.php index 47c2eca8896..7c81fc76326 100644 --- a/admin/tool/mobile/classes/api.php +++ b/admin/tool/mobile/classes/api.php @@ -589,66 +589,64 @@ class api { $warnings = array(); - $curl = new curl(); - // Return certificate information and verify the certificate. - $curl->setopt(array('CURLOPT_CERTINFO' => 1, 'CURLOPT_SSL_VERIFYPEER' => true)); - $httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); // Force https url. - // Check https using a page not redirecting or returning exceptions. - $curl->head($httpswwwroot . "/$CFG->admin/tool/mobile/mobile.webmanifest.php"); - $info = $curl->get_info(); + if (is_https()) { + $curl = new curl(); + // Return certificate information and verify the certificate. + $curl->setopt(array('CURLOPT_CERTINFO' => 1, 'CURLOPT_SSL_VERIFYPEER' => true)); + // Check https using a page not redirecting or returning exceptions. + $curl->head("$CFG->wwwroot/$CFG->admin/tool/mobile/mobile.webmanifest.php"); + $info = $curl->get_info(); - // First of all, check the server certificate (if any). - if (empty($info['http_code']) or ($info['http_code'] >= 400)) { - $warnings[] = ['nohttpsformobilewarning', 'admin']; - } else { // Check the certificate is not self-signed or has an untrusted-root. // This may be weak in some scenarios (when the curl SSL verifier is outdated). - if (empty($info['certinfo'])) { + if (empty($info['http_code']) || empty($info['certinfo'])) { $warnings[] = ['selfsignedoruntrustedcertificatewarning', 'tool_mobile']; } else { $timenow = time(); - $expectedissuer = null; - foreach ($info['certinfo'] as $cert) { + $infokeys = array_keys($info['certinfo']); + $lastkey = end($infokeys); + + if (count($info['certinfo']) == 1) { + // This will work in a normal browser because it will complete the chain, but not in a mobile app. + $warnings[] = ['invalidcertificatechainwarning', 'tool_mobile']; + } + + foreach ($info['certinfo'] as $key => $cert) { + // Convert to lower case the keys, some OS/curl implementations differ. + $cert = array_change_key_case($cert, CASE_LOWER); // Due to a bug in certain curl/openssl versions the signature algorithm isn't always correctly parsed. // See https://github.com/curl/curl/issues/3706 for reference. - if (!array_key_exists('Signature Algorithm', $cert)) { + if (!array_key_exists('signature algorithm', $cert)) { // The malformed field that does contain the algorithm we're looking for looks like the following: // Signature Algorithm: . - preg_match('/\s+Signature Algorithm: (?[^\s]+)/', $cert['Public Key Algorithm'], $matches); + preg_match('/\s+Signature Algorithm: (?[^\s]+)/', $cert['public key algorithm'], $matches); $signaturealgorithm = $matches['algorithm'] ?? ''; } else { - $signaturealgorithm = $cert['Signature Algorithm']; + $signaturealgorithm = $cert['signature algorithm']; } // Check if the signature algorithm is weak (Android won't work with SHA-1). - if ($signaturealgorithm == 'sha1WithRSAEncryption' || $signaturealgorithm == 'sha1WithRSA') { - $warnings[] = ['insecurealgorithmwarning', 'tool_mobile']; + if ($key != $lastkey && + ($signaturealgorithm == 'sha1WithRSAEncryption' || $signaturealgorithm == 'sha1WithRSA')) { + $warnings['insecurealgorithmwarning'] = ['insecurealgorithmwarning', 'tool_mobile']; } // Check certificate start date. - if (strtotime($cert['Start date']) > $timenow) { - $warnings[] = ['invalidcertificatestartdatewarning', 'tool_mobile']; + if (strtotime($cert['start date']) > $timenow) { + $warnings['invalidcertificatestartdatewarning'] = ['invalidcertificatestartdatewarning', 'tool_mobile']; } // Check certificate end date. - if (strtotime($cert['Expire date']) < $timenow) { - $warnings[] = ['invalidcertificateexpiredatewarning', 'tool_mobile']; + if (strtotime($cert['expire date']) < $timenow) { + $warnings['invalidcertificateexpiredatewarning'] = ['invalidcertificateexpiredatewarning', 'tool_mobile']; } - // Check the chain. - if ($expectedissuer !== null) { - if ($expectedissuer !== $cert['Subject'] || $cert['Subject'] === $cert['Issuer']) { - $warnings[] = ['invalidcertificatechainwarning', 'tool_mobile']; - } - } - $expectedissuer = $cert['Issuer']; } } + } else { + // Warning for non https sites. + $warnings[] = ['nohttpsformobilewarning', 'admin']; } - // Now check typical configuration problems. - if ((int) $CFG->userquota === PHP_INT_MAX) { - // In old Moodle version was a text so was possible to have numeric values > PHP_INT_MAX. - $warnings[] = ['invaliduserquotawarning', 'tool_mobile']; - } + // Check ADOdb debug enabled. if (get_config('auth_db', 'debugauthdb') || get_config('enrol_database', 'debugdb')) { $warnings[] = ['adodbdebugwarning', 'tool_mobile']; diff --git a/admin/tool/mobile/lang/en/tool_mobile.php b/admin/tool/mobile/lang/en/tool_mobile.php index 6a2ac04ff4d..8604fd1e14f 100644 --- a/admin/tool/mobile/lang/en/tool_mobile.php +++ b/admin/tool/mobile/lang/en/tool_mobile.php @@ -73,7 +73,7 @@ $string['forcelogout_desc'] = 'If enabled, the app option \'Change site\' is rep $string['h5poffline'] = 'View H5P content offline'; $string['httpsrequired'] = 'HTTPS required'; $string['insecurealgorithmwarning'] = 'It seems that the HTTPS certificate uses an insecure algorithm for signing (SHA-1). Please try updating the certificate.'; -$string['invalidcertificatechainwarning'] = 'It seems that the certificate chain is invalid.'; +$string['invalidcertificatechainwarning'] = 'It seems that the certificate chain is invalid. This certificate might work for a browser but not for a mobile app.'; $string['invalidcertificateexpiredatewarning'] = 'It seems that the HTTPS certificate for the site has expired.'; $string['invalidcertificatestartdatewarning'] = 'It seems that the HTTPS certificate for the site is not yet valid (with a start date in the future).'; $string['invalidprivatetoken'] = 'Invalid private token. Token should not be empty or passed via GET parameter.'; @@ -124,7 +124,7 @@ $string['qrcodetypelogin'] = 'QR code with automatic login'; $string['readingthisemailgettheapp'] = 'Reading this in an email? Download the mobile app and receive notifications on your mobile device.'; $string['remoteaddons'] = 'Remote add-ons'; $string['scanqrcode'] = 'Scan QR code'; -$string['selfsignedoruntrustedcertificatewarning'] = 'It seems that the HTTPS certificate is self-signed or not trusted. The mobile app will only work with trusted sites.'; +$string['selfsignedoruntrustedcertificatewarning'] = 'It seems that the HTTPS certificate is self-signed or not trusted. The mobile app will only work with trusted sites. Please, use any online SSL checker tool to further diagnose the problem. If these tools indicate that you certificate is OK, you can ignore this warning.'; $string['setuplink'] = 'App download page'; $string['setuplink_desc'] = 'URL of page with options to download the mobile app from the App Store and Google Play. The app download page link is displayed in the page footer and in a user\'s profile. Leave blank to not display a link.'; $string['smartappbanners'] = 'App Banners'; diff --git a/admin/tool/mobile/tests/api_test.php b/admin/tool/mobile/tests/api_test.php index e9fc4f4018e..9099b12ee45 100644 --- a/admin/tool/mobile/tests/api_test.php +++ b/admin/tool/mobile/tests/api_test.php @@ -78,7 +78,7 @@ class tool_mobile_api_testcase extends externallib_advanced_testcase { $CFG->debugdisplay = 1; set_config('debugauthdb', 1, 'auth_db'); set_config('debugdb', 1, 'enrol_database'); - $expectedissues = array('nohttpsformobilewarning', 'invaliduserquotawarning', 'adodbdebugwarning', 'displayerrorswarning'); + $expectedissues = array('adodbdebugwarning', 'displayerrorswarning'); $issues = api::get_potential_config_issues(); $this->assertCount(count($expectedissues), $issues);